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/ 
 ************/
"""
# * Move a char into a room.
from merc import *
from const import *
from magic import spell_null
from fight import stop_fighting

depth = 0  
class handler_ch:
    def to_room(ch, pRoomIndex):
        if not pRoomIndex:
            print ("Char_to_room: None.")
            room = room_index_hash[ROOM_VNUM_TEMPLE]
            ch.to_room(room)
            return

        ch.in_room = pRoomIndex
        pRoomIndex.people.append(ch)

        if not IS_NPC(ch):
            if ch.in_room.area.empty:
                ch.in_room.area.empty = False
                ch.in_room.area.age = 0
            
            ch.in_room.area.nplayer += 1

        obj = ch.get_eq(WEAR_LIGHT)

        if obj and obj.item_type == ITEM_LIGHT and obj.value[2] != 0:
            ch.in_room.light += 1

        
        if IS_AFFECTED(ch, AFF_PLAGUE):
            af = [af for af in ch.affected if af.type == 'plague']
            if not af:
                REMOVE_BIT(ch.affected_by, AFF_PLAGUE)
                return
            af = af[0]
            
            if af.level == 1:
                return
            plague = AFFECT_DATA()
            plague.where = TO_AFFECTS
            plague.type = "plague"
            plague.level = af.level - 1 
            plague.duration = random.randint(1, 2 * plague.level)
            plague.location = APPLY_STR
            plague.modifier = -5
            plague.bitvector = AFF_PLAGUE
            
            for vch in ch.in_room.people[:]:
                if not saves_spell(plague.level - 2, vch, DAM_DISEASE) \
                and not IS_IMMORTAL(vch) and not IS_AFFECTED(vch, AFF_PLAGUE) \
                and random.randint(0,5) == 0:
                    vch.send("You feel hot and feverish.\n\r")
                    act("$n shivers and looks very ill.",vch,None,None,TO_ROOM)
                    vch.affect_join(plague)
        return

    #
    # * Give an affect to a char.
    def affect_add(ch, paf):
        paf_new = AFFECT_DATA()
        paf_new.__dict__ = paf.__dict__.copy()
        ch.affected.append(paf_new)
        ch.affect_modify(paf_new, True)
        return
       
    #
    # * Apply or remove an affect to a character.
    def affect_modify(ch, paf, fAdd):
        mod = paf.modifier
        if fAdd:
            if paf.where == TO_AFFECTS:
                SET_BIT(ch.affected_by, paf.bitvector)
            elif paf.where == TO_IMMUNE:
                SET_BIT(ch.imm_flags, paf.bitvector)
            elif paf.where == TO_RESIST:
                SET_BIT(ch.res_flags, paf.bitvector)
            elif paf.where == TO_VULN:
                SET_BIT(ch.vuln_flags, paf.bitvector)
        else:
            if paf.where == TO_AFFECTS:
                REMOVE_BIT(ch.affected_by, paf.bitvector)
            elif paf.where == TO_IMMUNE:
                REMOVE_BIT(ch.imm_flags, paf.bitvector)
            elif paf.where == TO_RESIST:
                REMOVE_BIT(ch.res_flags, paf.bitvector)
            elif paf.where == TO_VULN:
                REMOVE_BIT(ch.vuln_flags, paf.bitvector)
            mod = 0 - mod

        
        if paf.location == APPLY_NONE: pass
        elif paf.location == APPLY_STR: ch.mod_stat[STAT_STR] += mod
        elif paf.location == APPLY_DEX: ch.mod_stat[STAT_DEX] += mod
        elif paf.location == APPLY_INT: ch.mod_stat[STAT_INT] += mod
        elif paf.location == APPLY_WIS: ch.mod_stat[STAT_WIS] += mod
        elif paf.location == APPLY_CON: ch.mod_stat[STAT_CON] += mod
        elif paf.location == APPLY_SEX: ch.sex += mod
        elif paf.location == APPLY_CLASS: pass
        elif paf.location == APPLY_LEVEL: pass
        elif paf.location == APPLY_AGE: pass
        elif paf.location == APPLY_HEIGHT: pass
        elif paf.location == APPLY_WEIGHT: pass
        elif paf.location == APPLY_MANA: ch.max_mana += mod
        elif paf.location == APPLY_HIT: ch.max_hit += mod
        elif paf.location == APPLY_MOVE: ch.max_move += mod
        elif paf.location == APPLY_GOLD: pass
        elif paf.location == APPLY_EXP: pass
        elif paf.location == APPLY_AC:
            for i in range(4):
                ch.armor[i] += mod
        elif paf.location == APPLY_HITROLL: ch.hitroll += mod
        elif paf.location == APPLY_DAMROLL: ch.damroll += mod
        elif paf.location == APPLY_SAVES: ch.saving_throw += mod
        elif paf.location == APPLY_SAVING_ROD: ch.saving_throw += mod
        elif paf.location == APPLY_SAVING_PETRI: ch.saving_throw += mod
        elif paf.location == APPLY_SAVING_BREATH: ch.saving_throw += mod
        elif paf.location == APPLY_SAVING_SPELL: ch.saving_throw += mod
        elif paf.location == APPLY_SPELL_AFFECT: pass
        else:
            print ("Affect_modify: unknown location %d." % paf.location)
            return
        #
        # * Check for weapon wielding.
        # * Guard against recursion (for weapons with affects).
        wield = ch.get_eq(WEAR_WIELD)
        if not IS_NPC(ch) and wield and wield.get_weight() > (str_app[ch.get_curr_stat(STAT_STR)].wield*10):
            global depth

            if depth == 0:
                depth += 1
                act("You drop $p.", ch, wield, None, TO_CHAR)
                act("$n drops $p.", ch, wield, None, TO_ROOM)
                wield.from_char()
                wield.to_room(ch.in_room)
                depth -= 1
        return
    
    def check_immune(ch, dam_type):
        immune = -1
        defence = IS_NORMAL

        if dam_type is DAM_NONE:
            return immune

        if dam_type <= 3:
            if IS_SET(ch.imm_flags,IMM_WEAPON):
                defence = IS_IMMUNE
            elif IS_SET(ch.res_flags,RES_WEAPON):
                defence = IS_RESISTANT
            elif IS_SET(ch.vuln_flags,VULN_WEAPON):
                defence = IS_VULNERABLE
        else: # magical attack */
            if IS_SET(ch.imm_flags,IMM_MAGIC):
                defence = IS_IMMUNE
            elif IS_SET(ch.res_flags,RES_MAGIC):
                defence = IS_RESISTANT
            elif IS_SET(ch.vuln_flags,VULN_MAGIC):
                defence = IS_VULNERABLE

        bit = {  DAM_BASH:IMM_BASH,
                 DAM_PIERCE:IMM_PIERCE,
                 DAM_SLASH:IMM_SLASH,
                 DAM_FIRE:IMM_FIRE,
                 DAM_COLD:IMM_COLD,
                 DAM_LIGHTNING:IMM_LIGHTNING,
                 DAM_ACID:IMM_ACID,
                 DAM_POISON:IMM_POISON,
                 DAM_NEGATIVE:IMM_NEGATIVE,
                 DAM_HOLY:IMM_HOLY,
                 DAM_ENERGY:IMM_ENERGY,
                 DAM_MENTAL:IMM_MENTAL,
                 DAM_DISEASE:IMM_DISEASE,
                 DAM_DROWNING:IMM_DROWNING,
                 DAM_LIGHT:IMM_LIGHT,
                 DAM_CHARM:IMM_CHARM,
                 DAM_SOUND:IMM_SOUND }
        if dam_type not in bit:
            return defence
        bit = bit[dam_type]

        if IS_SET(ch.imm_flags,bit):
            immune = IS_IMMUNE
        elif IS_SET(ch.res_flags,bit) and immune is not IS_IMMUNE:
            immune = IS_RESISTANT
        elif IS_SET(ch.vuln_flags,bit):
            if immune == IS_IMMUNE:
                immune = IS_RESISTANT
            elif immune == IS_RESISTANT:
                immune = IS_NORMAL
        else:
            immune = IS_VULNERABLE

        if immune == -1:
            return defence
        else:
            return immune    

    #* Retrieve a character's trusted level for permission checking.
    def get_trust(ch):
        if ch.desc and ch.desc.original:
            ch = ch.desc.original

        if ch.trust:
            return ch.trust

        if IS_NPC(ch) and ch.level >= LEVEL_HERO:
            return LEVEL_HERO - 1
        else:
            return ch.level
    # used to de-screw characters */
    def reset(ch):
        if IS_NPC(ch):
            return

        if ch.pcdata.perm_hit == 0 \
        or  ch.pcdata.perm_mana == 0 \
        or  ch.pcdata.perm_move == 0 \
        or  ch.pcdata.last_level == 0:
            # do a FULL reset */
            for loc in range(MAX_WEAR):
                obj = ch.get_eq(loc)
                if not obj:
                    continue
                affected = obj.affected
                if not obj.enchanted:
                    affected.extend(obj.pIndexData.affected)
                for af in affected:
                    mod = af.modifier
                    if af.location == APPLY_SEX:
                        ch.sex -= mod
                        if ch.sex < 0 or ch.sex > 2:
                            ch.sex = 0 if IS_NPC(ch) else ch.pcdata.true_sex
                    elif af.location == APPLY_MANA:    
                        ch.max_mana -= mod     
                    elif af.location == APPLY_HIT: 
                        ch.max_hit -= mod     
                    elif af.location == APPLY_MOVE:    
                        ch.max_move -= mod
            # now reset the permanent stats */
            ch.pcdata.perm_hit = ch.max_hit
            ch.pcdata.perm_mana = ch.max_mana
            ch.pcdata.perm_move = ch.max_move
            ch.pcdata.last_level = ch.played/3600
            if ch.pcdata.true_sex < 0 or ch.pcdata.true_sex > 2:
                if ch.sex > 0 and ch.sex < 3:
                    ch.pcdata.true_sex = ch.sex
                else:
                    ch.pcdata.true_sex = 0

        # now restore the character to his/her true condition */
        for stat in range(MAX_STATS):
            ch.mod_stat[stat] = 0

        if ch.pcdata.true_sex < 0 or ch.pcdata.true_sex > 2:
            ch.pcdata.true_sex = 0 
        ch.sex = ch.pcdata.true_sex
        ch.max_hit = ch.pcdata.perm_hit
        ch.max_mana = ch.pcdata.perm_mana
        ch.max_move = ch.pcdata.perm_move
       
        for i in range(4):
            ch.armor[i] = 100

        ch.hitroll = 0
        ch.damroll = 0
        ch.saving_throw = 0

        # now start adding back the effects */
        for loc in range(MAX_WEAR):
            obj = ch.get_eq(loc)
            if not obj:
                continue
            for i in range(4):
                ch.armor[i] -= obj.apply_ac(loc, i)
            affected = obj.affected
            if not obj.enchanted:
                affected.extend(obj.pIndexData.affected)

            for af in affected:
                mod = af.modifier
                if af.location == APPLY_STR:
                    ch.mod_stat[STAT_STR] += mod
                elif af.location == APPLY_DEX:
                    ch.mod_stat[STAT_DEX] += mod
                elif af.location == APPLY_INT:
                    ch.mod_stat[STAT_INT] += mod
                elif af.location == APPLY_WIS:
                    ch.mod_stat[STAT_WIS] += mod
                elif af.location == APPLY_CON:
                    ch.mod_stat[STAT_CON] += mod
                elif af.location == APPLY_SEX:
                    ch.sex += mod
                elif af.location == APPLY_MANA:
                    ch.max_mana += mod
                elif af.location == APPLY_HIT:
                    ch.max_hit += mod
                elif af.location == APPLY_MOVE:
                    ch.max_move += mod
                elif af.location == APPLY_AC:
                    ch.armor = [i+mod for i in ch.armor]
                elif af.location == APPLY_HITROLL:
                    ch.hitroll += mod
                elif af.location == APPLY_DAMROLL:
                    ch.damroll += mod
                elif af.location == APPLY_SAVES:
                    ch.saving_throw += mod
                elif af.location == APPLY_SAVING_ROD:
                    ch.saving_throw += mod
                elif af.location == APPLY_SAVING_PETRI:
                    ch.saving_throw += mod
                elif af.location == APPLY_SAVING_BREATH:
                    ch.saving_throw += mod
                elif af.location == APPLY_SAVING_SPELL:
                    ch.saving_throw += mod

       
        # now add back spell effects */
        for af in ch.affected:
            mod = af.modifier
            if af.location == APPLY_STR:
                ch.mod_stat[STAT_STR] += mod
            elif af.location == APPLY_DEX:
                ch.mod_stat[STAT_DEX] += mod
            elif af.location == APPLY_INT:
                ch.mod_stat[STAT_INT] += mod
            elif af.location == APPLY_WIS:
                ch.mod_stat[STAT_WIS] += mod
            elif af.location == APPLY_CON:
                ch.mod_stat[STAT_CON] += mod
            elif af.location == APPLY_SEX:
                ch.sex += mod
            elif af.location == APPLY_MANA:
                ch.max_mana += mod
            elif af.location == APPLY_HIT:
                ch.max_hit += mod
            elif af.location == APPLY_MOVE:
                ch.max_move += mod
            elif af.location == APPLY_AC:
                ch.armor = [ i+mod for i in ch.armor]
            elif af.location == APPLY_HITROLL:
                ch.hitroll     += mod
            elif af.location == APPLY_DAMROLL:
                ch.damroll     += mod
            elif af.location == APPLY_SAVES:
                ch.saving_throw += mod
            elif af.location == APPLY_SAVING_ROD:
                ch.saving_throw += mod
            elif af.location == APPLY_SAVING_PETRI:
                ch.saving_throw += mod
            elif af.location == APPLY_SAVING_BREATH:
                ch.saving_throw += mod
            elif af.location == APPLY_SAVING_SPELL:
                ch.saving_throw += mod
        # make sure sex is RIGHT!!!! */
        if ch.sex < 0 or ch.sex > 2:
            ch.sex = ch.pcdata.true_sex

    #Find a piece of eq on a character.
    def get_eq(ch, iWear):
        if not ch:
            return None
        objs = [obj for obj in ch.carrying if obj.wear_loc == iWear]
        if not objs:
            return None
        return objs[0]

    # * Equip a char with an obj.
    def equip(ch, obj, iWear):
        if ch.get_eq(iWear):
            print("Equip_char: already equipped (%d)." % iWear)
            return
        
        if (IS_OBJ_STAT(obj, ITEM_ANTI_EVIL) and IS_EVIL(ch)) \
        or (IS_OBJ_STAT(obj, ITEM_ANTI_GOOD) and IS_GOOD(ch)) \
        or (IS_OBJ_STAT(obj, ITEM_ANTI_NEUTRAL) and IS_NEUTRAL(ch)):
            # Thanks to Morgenes for the bug fix here!
            act("You are zapped by $p and drop it.", ch, obj, None, TO_CHAR)
            act("$n is zapped by $p and drops it.",  ch, obj, None, TO_ROOM)
            obj.from_char()
            obj.to_room(ch.in_room)
            return

        for i in range(4):
            ch.armor[i] -= obj.apply_ac(iWear, i)
        obj.wear_loc = iWear

        if not obj.enchanted:
            for paf in obj.pIndexData.affected:
                if paf.location != APPLY_SPELL_AFFECT:
                    ch.affect_modify(paf, True)
        
        for paf in obj.affected:
            if paf.location == APPLY_SPELL_AFFECT:
                affect_to_char (ch, paf)
            else:
                ch.affect_modify(paf, True)

        if obj.item_type == ITEM_LIGHT and obj.value[2] != 0 and ch.in_room != None:
            ch.in_room.light += 1
        return


    #
    # * Unequip a char with an obj.
    def unequip(ch, obj):
        if obj.wear_loc == WEAR_NONE:
            print ("Unequip_char: already unequipped.")
            return

        for i in range(4):
            ch.armor[i] += obj.apply_ac(obj.wear_loc, i)
        obj.wear_loc = -1

        if not obj.enchanted:
            for paf in obj.pIndexData.affected:
                if paf.location == APPLY_SPELL_AFFECT:
                    for lpaf in ch.affected[:]:
                        if lpaf.type == paf.type and lpaf.level == paf.level and lpaf.location == APPLY_SPELL_AFFECT:
                            ch.affect_remove(lpaf)
                            break
                else:
                    ch.affect_modify(paf, False)
                    ch.affect_check(paf.where, paf.bitvector)

        for paf in obj.affected:
            if paf.location == APPLY_SPELL_AFFECT:
                print ("Bug: Norm-Apply")
                for lpaf in ch.affected:
                    if lpaf.type == paf.type and lpaf.level == paf.level and lpaf.location == APPLY_SPELL_AFFECT:
                        print("bug: location = %d" % lpaf.location)
                        print("bug: type = %d" % lpaf.type)
                        ch.affect_remove(lpaf)
                        break
            else:
                ch.affect_modify(paf, False)
                ch.affect_check(paf.where, paf.bitvector) 

        if obj.item_type == ITEM_LIGHT \
        and obj.value[2] != 0 \
        and ch.in_room \
        and ch.in_room.light > 0:
            ch.in_room.light -= 1
        return

    # fix object affects when removing one */
    def affect_check(ch, where, vector):
        if where == TO_OBJECT or where == TO_WEAPON or vector == 0:
            return

        for paf in ch.affected:
            if paf.where == where and paf.bitvector == vector:
                if where == TO_AFFECTS:
                    SET_BIT(ch.affected_by, vector)
                elif where == TO_IMMUNE:
                    SET_BIT(ch.imm_flags, vector)   
                elif where == TO_RESIST:
                    SET_BIT(ch.res_flags, vector)
                elif where == TO_VULN:
                    SET_BIT(ch.vuln_flags, vector)
                return

        for obj in ch.carrying:
            if obj.wear_loc == -1:
                continue
            for paf in obj.affected:
                if paf.where == where and paf.bitvector == vector:
                    if where == TO_AFFECTS:
                        SET_BIT(ch.affected_by, vector)
                    elif where == TO_IMMUNE:
                        SET_BIT(ch.imm_flags, vector)   
                    elif where == TO_RESIST:
                        SET_BIT(ch.res_flags, vector)
                    elif where == TO_VULN:
                        SET_BIT(ch.vuln_flags, vector)
                    return
            if obj.enchanted:
                continue
            for paf in obj.pIndexData.affected:
                if paf.where == where and paf.bitvector == vector:
                    if where == TO_AFFECTS:
                        SET_BIT(ch.affected_by, vector)
                    elif where == TO_IMMUNE:
                        SET_BIT(ch.imm_flags, vector)   
                    elif where == TO_RESIST:
                        SET_BIT(ch.res_flags, vector)
                    elif where == TO_VULN:
                        SET_BIT(ch.vuln_flags, vector)
                    return
                    
    # * Remove an affect from a char.
    def affect_remove(ch, paf):
        if not ch.affected:
            print ("BUG: Affect_remove: no affect.")
            return

        ch.affect_modify(paf, False)
        where = paf.where
        vector = paf.bitvector
        
        if paf not in ch.affected:
            print ("Affect_remove: cannot find paf.")
            return
        ch.affected.remove(paf)
        del paf
        ch.affect_check(where, vector)
        return

    # * Strip all affects of a given sn.
    def affect_strip(ch, sn):
        [ch.affect_remove(paf) for paf in ch.affected[:] if paf.type == sn]
        return

    #
    # * Add or enhance an affect.
    def affect_join(ch, paf):
        found = False
        for paf_old in ch.affected:
            if paf_old.type == paf.type:
                paf.level = (paf.level + paf_old.level) / 2
                paf.duration += paf_old.duration
                paf.modifier += paf_old.modifier
                ch.affect_remove(paf_old)
                break
        
        ch.affect_add(paf)
        return
    # * Move a char out of a room.
    def from_room(ch):
        if not ch.in_room:
            print("BUG: Char_from_room: None.")
            return

        if not IS_NPC(ch):
            ch.in_room.area.nplayer -= 1
        obj = ch.get_eq(WEAR_LIGHT)
        if obj and obj.item_type == ITEM_LIGHT and obj.value[2] != 0 and ch.in_room.light > 0:
            ch.in_room.light -= 1


        if ch not in ch.in_room.people:
            print("BUG: Char_from_room: ch not found.")
            return
        ch.in_room.people.remove(ch)
        ch.in_room = None
        ch.on = None  # sanity check! */
        return

    # * Move a char into a room.
    def to_room(ch, pRoomIndex):
        if not pRoomIndex:
            print("Char_to_room: None.")
            room = room_index_hash[ROOM_VNUM_TEMPLE]
            ch.to_room(room)
            return

        ch.in_room = pRoomIndex
        pRoomIndex.people.append(ch)

        if not IS_NPC(ch):
            if ch.in_room.area.empty:
                ch.in_room.area.empty = False
                ch.in_room.area.age = 0
            
            ch.in_room.area.nplayer += 1

        obj = ch.get_eq(WEAR_LIGHT)

        if obj and obj.item_type == ITEM_LIGHT and obj.value[2] != 0:
            ch.in_room.light += 1

        
        if IS_AFFECTED(ch,AFF_PLAGUE):
            af = [af for af in ch.affected if af.type == 'plague']
            if not af:
                REMOVE_BIT(ch.affected_by,AFF_PLAGUE)
                return
            af = af[0]
            
            if af.level == 1:
                return
            plague = AFFECT_DATA()
            plague.where = TO_AFFECTS
            plague.type = "plague"
            plague.level = af.level - 1 
            plague.duration = random.randint(1,2 * plague.level)
            plague.location = APPLY_STR
            plague.modifier = -5
            plague.bitvector = AFF_PLAGUE
            
            for vch in ch.in_room.people[:]:
                if not saves_spell(plague.level - 2,vch,DAM_DISEASE) and not IS_IMMORTAL(vch) and not IS_AFFECTED(vch,AFF_PLAGUE) and random.randint(0,5) == 0:
                    vch.send("You feel hot and feverish.\n\r")
                    act("$n shivers and looks very ill.",vch,None,None,TO_ROOM)
                    vch.affect_join(plague)
        return

    # * Extract a char from the world.
    def extract(ch, fPull):
        # doesn't seem to be necessary
        #if not ch.in_room:
        #    print "Extract_char: None."
        #    return
     
    #    nuke_pets(ch)
        ch.pet = None # just in case */

        #if fPull:
        #    die_follower( ch )
        stop_fighting( ch, True )

        for obj in ch.carrying[:]:
            obj.extract()
        
        if ch.in_room:
            ch.from_room()

        # Death room is set in the clan tabe now */
        if not fPull:
            ch.to_room(room_index_hash[ch.clan.hall])
            return

        if ch.desc and ch.desc.original:
            ch.do_return("")
            ch.desc = None

        for wch in player_list:
            if wch.reply == ch:
                wch.reply = None

        if ch not in char_list:
            print ("Extract_char: char not found.")
            return
        
        char_list.remove(ch)
        if ch in player_list:
            player_list.remove(ch)

        if ch.desc:
            ch.desc.character = None
        return

    # * Find a char in the room.
    def get_char_room(ch, argument):
        number, arg = number_argument(argument)
        count  = 0
        arg = arg.lower()
        if arg == "self":
            return ch
        for rch in ch.in_room.people:
            if not ch.can_see(rch):
                continue
            if not IS_NPC(rch) and not rch.name.lower().startswith(arg):
                continue
            if IS_NPC(rch) and not is_name(arg, rch.name):
                continue
            count += 1
            if count == number:
                return rch
        return None

    # * Find a char in the world.
    def get_char_world(ch, argument):
        wch = ch.get_char_room(argument)
        if wch:
            return wch

        number, arg = number_argument(argument)
        count  = 0
        for wch in char_list:
            if wch.in_room == None or not ch.can_see(wch):
                continue
            if not IS_NPC(wch) and not is_name(arg, wch.name.lower()):
                continue
            if IS_NPC(wch) and arg not in wch.name:
                continue
            count += 1
            if count == number:
                return wch
        return None

    # * Find an obj in a list.
    def get_obj_list(ch, argument, contents):
        number, arg = number_argument(argument)
        count  = 0
        for obj in contents:
            if ch.can_see_obj(obj) and is_name(arg, obj.name.lower()):
                count += 1
                if count == number:
                    return obj
        return None

    # * Find an obj in player's inventory.
    def get_obj_carry(ch, argument, viewer):
        number, arg = number_argument(argument)
        count  = 0
        for obj in ch.carrying:
            if obj.wear_loc == WEAR_NONE and viewer.can_see_obj(obj) and is_name(arg, obj.name.lower()):
                count += 1
                if count == number:
                    return obj
        return None

    # * Find an obj in player's equipment.
    def get_obj_wear(ch, argument):
        number, arg = number_argument(argument)
        count = 0
        for obj in ch.carrying:
            if obj.wear_loc != WEAR_NONE and ch.can_see_obj(obj) and is_name(arg, obj.name.lower()):
                count += 1   
                if count == number:
                    return obj
        return None

    # * Find an obj in the room or in inventory.
    def get_obj_here(ch, argument):
        obj = ch.get_obj_list(argument, ch.in_room.contents)
        if obj:
            return obj
        obj = ch.get_obj_carry(argument, ch)
        if obj:
            return obj
        obj = ch.get_obj_wear(argument)
        if obj:
            return obj
        return None

    # * Find an obj in the world.
    def get_obj_world(ch, argument):
        obj = ch.get_obj_here(argument)
        if obj:
            return obj

        number, arg = number_argument(argument)
        count = 0
        arg = arg.lower()
        for obj in object_list:
            if ch.can_see_obj(obj) and is_name(arg, obj.name.lower()):
                count += 1
            if count == number:
               return obj
        return None

    # deduct cost from a character */
    def deduct_cost(ch, cost):
        silver = min(ch.silver, cost) 
        gold = 0
        if silver < cost:
            gold = ((cost - silver + 99) / 100)
            silver = cost - 100 * gold
        ch.gold -= gold
        ch.silver -= silver

        if ch.gold < 0:
            print ("Bug: deduct costs: gold %d < 0" % ch.gold)
            ch.gold = 0
        if ch.silver < 0:
            print ("BUG: deduct costs: silver %d < 0" % ch.silver)
            ch.silver = 0
    
    def is_room_owner(ch, room):
        if not room.owner:
            return False
        return True if ch.name in room.owner else False

    # visibility on a room -- for entering and exits */
    def can_see_room(ch, pRoomIndex):
        if IS_SET(pRoomIndex.room_flags, ROOM_IMP_ONLY) and  ch.get_trust() < MAX_LEVEL:
            return False
        if IS_SET(pRoomIndex.room_flags, ROOM_GODS_ONLY) and not IS_IMMORTAL(ch):
            return False
        if IS_SET(pRoomIndex.room_flags, ROOM_HEROES_ONLY) and not IS_IMMORTAL(ch):
            return False
        if IS_SET(pRoomIndex.room_flags,ROOM_NEWBIES_ONLY) and ch.level > 5 and not IS_IMMORTAL(ch):
            return False
        if not IS_IMMORTAL(ch) and pRoomIndex.clan and ch.clan != pRoomIndex.clan:
            return False
        return True

    # * True if char can see victim.
    def can_see(ch, victim):
        # RT changed so that WIZ_INVIS has levels */
        if ch == victim:
            return True
        if ch.get_trust() < victim.invis_level:
            return False
        if ch.get_trust() < victim.incog_level and ch.in_room != victim.in_room:
            return False
        if (not IS_NPC(ch) and IS_SET(ch.act, PLR_HOLYLIGHT)) or (IS_NPC(ch) and IS_IMMORTAL(ch)):
            return True
        if IS_AFFECTED(ch, AFF_BLIND):
            return False
        if ch.in_room.is_dark() and not IS_AFFECTED(ch, AFF_INFRARED):
            return False
        if IS_AFFECTED(victim, AFF_INVISIBLE) and not IS_AFFECTED(ch, AFF_DETECT_INVIS):
            return False
        # sneaking */

        if IS_AFFECTED(victim, AFF_SNEAK) and not IS_AFFECTED(ch, AFF_DETECT_HIDDEN) and victim.fighting == None:
            chance = victim.get_skill("sneak")
            chance += victim.get_curr_stat(STAT_DEX) * 3 // 2
            chance -= ch.get_curr_stat(STAT_INT) * 2
            chance -= ch.level - victim.level * 3 // 2

            if random.randint(1,99) < chance:
                return False

        if IS_AFFECTED(victim, AFF_HIDE) and not IS_AFFECTED(ch, AFF_DETECT_HIDDEN) and victim.fighting == None:
            return False

        return True

    # * True if char can see obj.
    def can_see_obj(ch, obj):
        if not IS_NPC(ch) and IS_SET(ch.act, PLR_HOLYLIGHT):
            return True
        if IS_SET(obj.extra_flags, ITEM_VIS_DEATH):
            return False
        if IS_AFFECTED(ch, AFF_BLIND) and obj.item_type != ITEM_POTION:
            return False
        if obj.item_type == ITEM_LIGHT and obj.value[2] != 0:
            return True
        if IS_SET(obj.extra_flags, ITEM_INVIS) and not IS_AFFECTED(ch, AFF_DETECT_INVIS):
            return False
        if IS_OBJ_STAT(obj,ITEM_GLOW):
            return True
        if ch.in_room.is_dark() and not IS_AFFECTED(ch, AFF_DARK_VISION):
            return False
        return True

    # * True if char can drop obj.
    def can_drop_obj(ch, obj):
        if not IS_SET(obj.extra_flags, ITEM_NODROP):
            return True
        if not IS_NPC(ch) and ch.level >= LEVEL_IMMORTAL:
            return True
        return False

    # for returning skill information */
    def get_skill(ch, sn):
        skill = 0

        if sn == -1: # shorthand for level based skills */
            skill = ch.level * 5 / 2
        elif sn not in skill_table:
            print ("BUG: Bad sn %s in get_skill." % sn)
            skill = 0
        elif not IS_NPC(ch):
            if ch.level < skill_table[sn].skill_level[ch.guild.name] or sn not in ch.pcdata.learned:
                skill = 0
            else:
                skill = ch.pcdata.learned[sn]
        else: # mobiles */
            if skill_table[sn].spell_fun != spell_null:
                skill = 40 + 2 * ch.level;
            elif sn == 'sneak' or sn == 'hide':
                skill = ch.level * 2 + 20
            elif (sn == 'dodge' and IS_SET(ch.off_flags, OFF_DODGE)) \
            or (sn == 'parry' and IS_SET(ch.off_flags, OFF_PARRY)):
                skill = ch.level * 2
            elif sn == 'shield block':
                skill = 10 + 2 * ch.level
            elif sn == 'second attack' \
            and (IS_SET(ch.act, ACT_WARRIOR) or IS_SET(ch.act, ACT_THIEF)):
                skill = 10 + 3 * ch.level
            elif sn == 'third_attack' and IS_SET(ch.act, ACT_WARRIOR):
                skill = 4 * ch.level - 40
            elif sn == 'hand to hand':
                skill = 40 + 2 * ch.level
            elif sn == "trip" and IS_SET(ch.off_flags, OFF_TRIP):
                skill = 10 + 3 * ch.level
            elif sn == "bash" and IS_SET(ch.off_flags, OFF_BASH):
                skill = 10 + 3 * ch.level
            elif sn == "disarm" and (IS_SET(ch.off_flags, OFF_DISARM) \
            or IS_SET(ch.act, ACT_WARRIOR) or IS_SET(ch.act, ACT_THIEF)):
                skill = 20 + 3 * ch.level
            elif sn == "berserk" and IS_SET(ch.off_flags, OFF_BERSERK):
                skill = 3 * ch.level;
            elif sn == "kick":
                skill = 10 + 3 * ch.level
            elif sn == "backstab" and IS_SET(ch.act, ACT_THIEF):
                skill = 20 + 2 * ch.level
            elif sn == "rescue":
                skill = 40 + ch.level
            elif sn == "recall":
                skill = 40 + ch.level
            elif sn in ["sword", "dagger", "spear", "mace", "axe", "flail", "whip", "polearm"]:
                skill = 40 + 5 * ch.level / 2
            else:
                skill = 0
        if ch.daze > 0:
            if skill_table[sn].spell_fun != spell_null:
                skill /= 2
            else:
                skill = 2 * skill / 3
        if not IS_NPC(ch) and ch.pcdata.condition[COND_DRUNK] > 10:
            skill = 9 * skill / 10

        return max(0, min(skill, 100))

    # for returning weapon information */
    def get_weapon_sn(ch):
        sn = None
        wield = ch.get_eq(WEAR_WIELD)

        if not wield or wield.item_type != ITEM_WEAPON:
            sn = "hand to hand"
        return wield.value[0]

    def get_weapon_skill(ch, sn):
        skill = 0
        # -1 is exotic */
        if IS_NPC(ch):
            if sn == -1:
                skill = 3 * ch.level
            elif sn == "hand to hand":
                skill = 40 + 2 * ch.level
            else: 
                skill = 40 + 5 * ch.level / 2
        elif sn in ch.pcdata.learned:
            if sn == -1:
                skill = 3 * ch.level
            else:
                skill = ch.pcdata.learned[sn]
        return max(0, min(skill,100))

    # * Retrieve a character's age.
    def get_age(ch):
        return 17 + (ch.played + (int) (time.time() - ch.logon)) / 72000

    # * Retrieve a character's carry capacity.
    def can_carry_n(ch):
        if not IS_NPC(ch) and ch.level >= LEVEL_IMMORTAL:
            return 1000
        if IS_NPC(ch) and IS_SET(ch.act, ACT_PET):
            return 0
        return MAX_WEAR +  2 * ch.get_curr_stat(STAT_DEX) + ch.level

    # * Retrieve a character's carry capacity.
    def can_carry_w(ch):
        if not IS_NPC(ch) and ch.level >= LEVEL_IMMORTAL:
            return 10000000
        if IS_NPC(ch) and IS_SET(ch.act, ACT_PET):
            return 0
        return str_app[ch.get_curr_stat(STAT_STR)].carry * 10 + ch.level * 25

    #/* command for retrieving stats */
    def get_curr_stat(ch, stat):
        smax = 0
        if IS_NPC(ch) or ch.level > LEVEL_IMMORTAL:
            smax = 25
        else:
            smax = pc_race_table[ch.race.name].max_stats[stat] + 4

            if ch.guild.attr_prime == stat:
                smax += 2

            if ch.race == race_table["human"]:
                smax += 1

            smax = min(smax,25);
        return max(3, min(ch.perm_stat[stat] + ch.mod_stat[stat], smax))

    # command for returning max training score */
    def get_max_train(ch, stat):
        smax = 0
        if IS_NPC(ch) or ch.level > LEVEL_IMMORTAL:
            return 25

        smax = pc_race_table[ch.race.name].max_stats[stat]
        if ch.guild.attr_prime == stat:
            if ch.race == race_table["human"]:
                smax += 3
            else:
                smax += 2
        return min(smax,25)

    # * It is very important that this be an equivalence relation:
    # * (1) A ~ A
    # * (2) if A ~ B then B ~ A
    # * (3) if A ~ B  and B ~ C, then A ~ C
    def is_same_group(ch, bch):
        if ch == None or bch == None:
            return False

        if ch.leader != None: ch = ch.leader
        if bch.leader != None: bch = bch.leader
        return ch == bch

    def is_clan(ch):
        return ch.clan.name != ""

    def is_same_clan(ch, victim):
        if ch.clan.independent:
            return False
        else:
            return ch.clan == victim.clan

    def exp_per_level(ch, points):
        if IS_NPC(ch):
            return 1000

        expl = 1000
        inc = 500

        if points < 40:
            return 1000 * pc_race_table[ch.race.name].class_mult[ch.guild.name]/100 if pc_race_table[ch.race.name].class_mult[ch.guild.name] else 1

        # processing */
        points -= 40

        while points > 9:
            expl += inc
            points -= 10
            if points > 9:
                expl += inc
                inc = inc * 2
                points -= 10

        expl += points * inc / 10

        return expl * pc_race_table[ch.race.name].class_mult[ch.guild.name]/100

    def can_loot(ch, obj):
        if IS_IMMORTAL(ch):
            return True
        if not obj.owner or obj.owner == None:
            return True
        owner = None
        for wch in char_list:
            if wch.name == obj.owner:
                owner = wch
        if owner == None:
            return True
        if ch.name == owner.name:
            return True
        if not IS_NPC(owner) and IS_SET(owner.act,PLR_CANLOOT):
            return True
        if ch.is_same_group(owner):
            return True
        return False

methods = {d:f for d,f in handler_ch.__dict__.items() if not d.startswith('__')}
for m,f in methods.items():
    setattr(CHAR_DATA, m, f)