/*
 * RAM $Id: special.c 16 2008-10-22 04:42:21Z ram $
 */

/***************************************************************************
 *  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                     *
***************************************************************************/

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "merc.h"
#include "db.h"
#include "act.h"
#include "interp.h"
#include "magic.h"
#include "special.h"

/* the function table */
const struct spec_type  spec_table[] = {
    {"spec_breath_any", spec_breath_any},
    {"spec_breath_acid", spec_breath_acid},
    {"spec_breath_fire", spec_breath_fire},
    {"spec_breath_frost", spec_breath_frost},
    {"spec_breath_gas", spec_breath_gas},
    {"spec_breath_lightning", spec_breath_lightning},
    {"spec_cast_adept", spec_cast_adept},
    {"spec_cast_cleric", spec_cast_cleric},
    {"spec_cast_judge", spec_cast_judge},
    {"spec_cast_mage", spec_cast_mage},
    {"spec_cast_undead", spec_cast_undead},
    {"spec_executioner", spec_executioner},
    {"spec_fido", spec_fido},
    {"spec_guard", spec_guard},
    {"spec_janitor", spec_janitor},
    {"spec_mayor", spec_mayor},
    {"spec_poison", spec_poison},
    {"spec_thief", spec_thief},
    {"spec_nasty", spec_nasty},
    {"spec_troll_member", spec_troll_member},
    {"spec_ogre_member", spec_ogre_member},
    {"spec_patrolman", spec_patrolman},
    {NULL, NULL}
};

/*
 * Given a name, return the appropriate spec fun.
 */
SPEC_FUN               *spec_lookup( const char *name )
{
    int                     i;

    for ( i = 0; spec_table[i].name != NULL; i++ )
    {
        if ( LOWER( name[0] ) == LOWER( spec_table[i].name[0] )
             && !str_prefix( name, spec_table[i].name ) )
            return spec_table[i].function;
    }

    return 0;
}

const char             *spec_name( SPEC_FUN *function )
{
    int                     i;

    for ( i = 0; spec_table[i].function != NULL; i++ )
    {
        if ( function == spec_table[i].function )
            return spec_table[i].name;
    }

    return NULL;
}

/*
 * Core procedure for dragons.
 */
bool dragon( CHAR_DATA *ch, const char *spell_name )
{
    CHAR_DATA              *victim;
    CHAR_DATA              *v_next;
    int                     sn;

    if ( ch->position != POS_FIGHTING )
        return FALSE;

    for ( victim = ch->in_room->people; victim != NULL; victim = v_next )
    {
        v_next = victim->next_in_room;
        if ( victim->fighting == ch && number_bits( 3 ) == 0 )
            break;
    }

    if ( victim == NULL )
        return FALSE;

    if ( ( sn = skill_lookup( spell_name ) ) < 0 )
        return FALSE;
    ( *skill_table[sn].spell_fun ) ( sn, ch->level, ch, victim, TARGET_CHAR );
    return TRUE;
}

/*
 * Special procedures for mobiles.
 */
bool spec_breath_any( CHAR_DATA *ch )
{
    if ( ch->position != POS_FIGHTING )
        return FALSE;

    switch ( number_bits( 3 ) )
    {
        case 0:
            return spec_breath_fire( ch );
        case 1:
        case 2:
            return spec_breath_lightning( ch );
        case 3:
            return spec_breath_gas( ch );
        case 4:
            return spec_breath_acid( ch );
        case 5:
        case 6:
        case 7:
            return spec_breath_frost( ch );
    }

    return FALSE;
}

bool spec_breath_acid( CHAR_DATA *ch )
{
    return dragon( ch, "acid breath" );
}

bool spec_breath_fire( CHAR_DATA *ch )
{
    return dragon( ch, "fire breath" );
}

bool spec_breath_frost( CHAR_DATA *ch )
{
    return dragon( ch, "frost breath" );
}

bool spec_breath_gas( CHAR_DATA *ch )
{
    int                     sn;

    if ( ch->position != POS_FIGHTING )
        return FALSE;

    if ( ( sn = skill_lookup( "gas breath" ) ) < 0 )
        return FALSE;
    ( *skill_table[sn].spell_fun ) ( sn, ch->level, ch, NULL, TARGET_CHAR );
    return TRUE;
}

bool spec_breath_lightning( CHAR_DATA *ch )
{
    return dragon( ch, "lightning breath" );
}

bool spec_cast_adept( CHAR_DATA *ch )
{
    CHAR_DATA              *victim;
    CHAR_DATA              *v_next;

    if ( !IS_AWAKE( ch ) )
        return FALSE;

    for ( victim = ch->in_room->people; victim != NULL; victim = v_next )
    {
        v_next = victim->next_in_room;
        if ( victim != ch && can_see( ch, victim ) && number_bits( 1 ) == 0
             && !IS_NPC( victim ) && victim->level < 11 )
            break;
    }

    if ( victim == NULL )
        return FALSE;

    switch ( number_bits( 4 ) )
    {
        case 0:
            act( "$n utters the word 'abrazak'.", ch, NULL, NULL, TO_ROOM );
            spell_armor( skill_lookup( "armor" ), ch->level, ch, victim, TARGET_CHAR );
            return TRUE;

        case 1:
            act( "$n utters the word 'fido'.", ch, NULL, NULL, TO_ROOM );
            spell_bless( skill_lookup( "bless" ), ch->level, ch, victim, TARGET_CHAR );
            return TRUE;

        case 2:
            act( "$n utters the words 'judicandus noselacri'.", ch, NULL, NULL, TO_ROOM );
            spell_cure_blindness( skill_lookup( "cure blindness" ),
                                  ch->level, ch, victim, TARGET_CHAR );
            return TRUE;

        case 3:
            act( "$n utters the words 'judicandus dies'.", ch, NULL, NULL, TO_ROOM );
            spell_cure_light( skill_lookup( "cure light" ),
                              ch->level, ch, victim, TARGET_CHAR );
            return TRUE;

        case 4:
            act( "$n utters the words 'judicandus sausabru'.", ch, NULL, NULL, TO_ROOM );
            spell_cure_poison( skill_lookup( "cure poison" ),
                               ch->level, ch, victim, TARGET_CHAR );
            return TRUE;

        case 5:
            act( "$n utters the word 'candusima'.", ch, NULL, NULL, TO_ROOM );
            spell_refresh( skill_lookup( "refresh" ), ch->level, ch, victim,
                           TARGET_CHAR );
            return TRUE;

        case 6:
            act( "$n utters the words 'judicandus eugzagz'.", ch, NULL, NULL, TO_ROOM );
            spell_cure_disease( skill_lookup( "cure disease" ),
                                ch->level, ch, victim, TARGET_CHAR );
    }

    return FALSE;
}

bool spec_cast_cleric( CHAR_DATA *ch )
{
    CHAR_DATA              *victim;
    CHAR_DATA              *v_next;
    const char             *spell;
    int                     sn;

    if ( ch->position != POS_FIGHTING )
        return FALSE;

    for ( victim = ch->in_room->people; victim != NULL; victim = v_next )
    {
        v_next = victim->next_in_room;
        if ( victim->fighting == ch && number_bits( 2 ) == 0 )
            break;
    }

    if ( victim == NULL )
        return FALSE;

    for ( ;; )
    {
        int                     min_level;

        switch ( number_bits( 4 ) )
        {
            case 0:
                min_level = 0;
                spell = "blindness";
                break;
            case 1:
                min_level = 3;
                spell = "cause serious";
                break;
            case 2:
                min_level = 7;
                spell = "earthquake";
                break;
            case 3:
                min_level = 9;
                spell = "cause critical";
                break;
            case 4:
                min_level = 10;
                spell = "dispel evil";
                break;
            case 5:
                min_level = 12;
                spell = "curse";
                break;
            case 6:
                min_level = 12;
                spell = "change sex";
                break;
            case 7:
                min_level = 13;
                spell = "flamestrike";
                break;
            case 8:
            case 9:
            case 10:
                min_level = 15;
                spell = "harm";
                break;
            case 11:
                min_level = 15;
                spell = "plague";
                break;
            default:
                min_level = 16;
                spell = "dispel magic";
                break;
        }

        if ( ch->level >= min_level )
            break;
    }

    if ( ( sn = skill_lookup( spell ) ) < 0 )
        return FALSE;
    ( *skill_table[sn].spell_fun ) ( sn, ch->level, ch, victim, TARGET_CHAR );
    return TRUE;
}

bool spec_cast_judge( CHAR_DATA *ch )
{
    CHAR_DATA              *victim;
    CHAR_DATA              *v_next;
    const char             *spell;
    int                     sn;

    if ( ch->position != POS_FIGHTING )
        return FALSE;

    for ( victim = ch->in_room->people; victim != NULL; victim = v_next )
    {
        v_next = victim->next_in_room;
        if ( victim->fighting == ch && number_bits( 2 ) == 0 )
            break;
    }

    if ( victim == NULL )
        return FALSE;

    spell = "high explosive";
    if ( ( sn = skill_lookup( spell ) ) < 0 )
        return FALSE;
    ( *skill_table[sn].spell_fun ) ( sn, ch->level, ch, victim, TARGET_CHAR );
    return TRUE;
}

bool spec_cast_mage( CHAR_DATA *ch )
{
    CHAR_DATA              *victim;
    CHAR_DATA              *v_next;
    const char             *spell;
    int                     sn;

    if ( ch->position != POS_FIGHTING )
        return FALSE;

    for ( victim = ch->in_room->people; victim != NULL; victim = v_next )
    {
        v_next = victim->next_in_room;
        if ( victim->fighting == ch && number_bits( 2 ) == 0 )
            break;
    }

    if ( victim == NULL )
        return FALSE;

    for ( ;; )
    {
        int                     min_level;

        switch ( number_bits( 4 ) )
        {
            case 0:
                min_level = 0;
                spell = "blindness";
                break;
            case 1:
                min_level = 3;
                spell = "chill touch";
                break;
            case 2:
                min_level = 7;
                spell = "weaken";
                break;
            case 3:
                min_level = 8;
                spell = "teleport";
                break;
            case 4:
                min_level = 11;
                spell = "colour spray";
                break;
            case 5:
                min_level = 12;
                spell = "change sex";
                break;
            case 6:
                min_level = 13;
                spell = "energy drain";
                break;
            case 7:
            case 8:
            case 9:
                min_level = 15;
                spell = "fireball";
                break;
            case 10:
                min_level = 20;
                spell = "plague";
                break;
            default:
                min_level = 20;
                spell = "acid blast";
                break;
        }

        if ( ch->level >= min_level )
            break;
    }

    if ( ( sn = skill_lookup( spell ) ) < 0 )
        return FALSE;
    ( *skill_table[sn].spell_fun ) ( sn, ch->level, ch, victim, TARGET_CHAR );
    return TRUE;
}

bool spec_cast_undead( CHAR_DATA *ch )
{
    CHAR_DATA              *victim;
    CHAR_DATA              *v_next;
    const char             *spell;
    int                     sn;

    if ( ch->position != POS_FIGHTING )
        return FALSE;

    for ( victim = ch->in_room->people; victim != NULL; victim = v_next )
    {
        v_next = victim->next_in_room;
        if ( victim->fighting == ch && number_bits( 2 ) == 0 )
            break;
    }

    if ( victim == NULL )
        return FALSE;

    for ( ;; )
    {
        int                     min_level;

        switch ( number_bits( 4 ) )
        {
            case 0:
                min_level = 0;
                spell = "curse";
                break;
            case 1:
                min_level = 3;
                spell = "weaken";
                break;
            case 2:
                min_level = 6;
                spell = "chill touch";
                break;
            case 3:
                min_level = 9;
                spell = "blindness";
                break;
            case 4:
                min_level = 12;
                spell = "poison";
                break;
            case 5:
                min_level = 15;
                spell = "energy drain";
                break;
            case 6:
                min_level = 18;
                spell = "harm";
                break;
            case 7:
                min_level = 21;
                spell = "teleport";
                break;
            case 8:
                min_level = 20;
                spell = "plague";
                break;
            default:
                min_level = 18;
                spell = "harm";
                break;
        }

        if ( ch->level >= min_level )
            break;
    }

    if ( ( sn = skill_lookup( spell ) ) < 0 )
        return FALSE;
    ( *skill_table[sn].spell_fun ) ( sn, ch->level, ch, victim, TARGET_CHAR );
    return TRUE;
}

bool spec_executioner( CHAR_DATA *ch )
{
    char                    buf[MAX_STRING_LENGTH];
    CHAR_DATA              *victim;
    CHAR_DATA              *v_next;
    const char             *crime;

    if ( !IS_AWAKE( ch ) || ch->fighting != NULL )
        return FALSE;

    crime = "";
    for ( victim = ch->in_room->people; victim != NULL; victim = v_next )
    {
        v_next = victim->next_in_room;

        if ( !IS_NPC( victim ) && IS_SET( victim->act, PLR_KILLER )
             && can_see( ch, victim ) )
        {
            crime = "KILLER";
            break;
        }

        if ( !IS_NPC( victim ) && IS_SET( victim->act, PLR_THIEF )
             && can_see( ch, victim ) )
        {
            crime = "THIEF";
            break;
        }
    }

    if ( victim == NULL )
        return FALSE;

    sprintf( buf, "%s is a %s!  PROTECT THE INNOCENT!  MORE BLOOOOD!!!",
             victim->name, crime );
    REMOVE_BIT( ch->comm, COMM_NOSHOUT );
    do_function( ch, &do_yell, buf );
    multi_hit( ch, victim, TYPE_UNDEFINED );
    return TRUE;
}

bool spec_fido( CHAR_DATA *ch )
{
    OBJ_DATA               *corpse;
    OBJ_DATA               *c_next;
    OBJ_DATA               *obj;
    OBJ_DATA               *obj_next;

    if ( !IS_AWAKE( ch ) )
        return FALSE;

    for ( corpse = ch->in_room->contents; corpse != NULL; corpse = c_next )
    {
        c_next = corpse->next_content;
        if ( corpse->item_type != ITEM_CORPSE_NPC )
            continue;

        act( "$n savagely devours a corpse.", ch, NULL, NULL, TO_ROOM );
        for ( obj = corpse->contains; obj; obj = obj_next )
        {
            obj_next = obj->next_content;
            obj_from_obj( obj );
            obj_to_room( obj, ch->in_room );
        }
        extract_obj( corpse );
        return TRUE;
    }

    return FALSE;
}

bool spec_guard( CHAR_DATA *ch )
{
    char                    buf[MAX_STRING_LENGTH];
    CHAR_DATA              *victim;
    CHAR_DATA              *v_next;
    CHAR_DATA              *ech;
    const char             *crime;
    int                     max_evil;

    if ( !IS_AWAKE( ch ) || ch->fighting != NULL )
        return FALSE;

    max_evil = 300;
    ech = NULL;
    crime = "";

    for ( victim = ch->in_room->people; victim != NULL; victim = v_next )
    {
        v_next = victim->next_in_room;

        if ( !IS_NPC( victim ) && IS_SET( victim->act, PLR_KILLER )
             && can_see( ch, victim ) )
        {
            crime = "KILLER";
            break;
        }

        if ( !IS_NPC( victim ) && IS_SET( victim->act, PLR_THIEF )
             && can_see( ch, victim ) )
        {
            crime = "THIEF";
            break;
        }

        if ( victim->fighting != NULL
             && victim->fighting != ch && victim->alignment < max_evil )
        {
            max_evil = victim->alignment;
            ech = victim;
        }
    }

    if ( victim != NULL )
    {
        sprintf( buf, "%s is a %s!  PROTECT THE INNOCENT!!  BANZAI!!",
                 victim->name, crime );
        REMOVE_BIT( ch->comm, COMM_NOSHOUT );
        do_function( ch, &do_yell, buf );
        multi_hit( ch, victim, TYPE_UNDEFINED );
        return TRUE;
    }

    if ( ech != NULL )
    {
        act( "$n screams 'PROTECT THE INNOCENT!!  BANZAI!!", ch, NULL, NULL, TO_ROOM );
        multi_hit( ch, ech, TYPE_UNDEFINED );
        return TRUE;
    }

    return FALSE;
}

bool spec_janitor( CHAR_DATA *ch )
{
    OBJ_DATA               *trash;
    OBJ_DATA               *trash_next;

    if ( !IS_AWAKE( ch ) )
        return FALSE;

    for ( trash = ch->in_room->contents; trash != NULL; trash = trash_next )
    {
        trash_next = trash->next_content;
        if ( !IS_SET( trash->wear_flags, ITEM_TAKE ) || !can_loot( ch, trash ) )
            continue;
        if ( trash->item_type == ITEM_DRINK_CON
             || trash->item_type == ITEM_TRASH || trash->cost < 10 )
        {
            act( "$n picks up some trash.", ch, NULL, NULL, TO_ROOM );
            obj_from_room( trash );
            obj_to_char( trash, ch );
            return TRUE;
        }
    }

    return FALSE;
}

bool spec_mayor( CHAR_DATA *ch )
{
    static const char       open_path[] =
        "W3a3003b33000c111d0d111Oe333333Oe22c222112212111a1S.";

    static const char       close_path[] =
        "W3a3003b33000c111d0d111CE333333CE22c222112212111a1S.";

    static const char      *path;
    static int              pos;
    static bool             move;

    if ( !move )
    {
        if ( time_info.hour == 6 )
        {
            path = open_path;
            move = TRUE;
            pos = 0;
        }

        if ( time_info.hour == 20 )
        {
            path = close_path;
            move = TRUE;
            pos = 0;
        }
    }

    if ( ch->fighting != NULL )
        return spec_cast_mage( ch );
    if ( !move || ch->position < POS_SLEEPING )
        return FALSE;

    switch ( path[pos] )
    {
        case '0':
        case '1':
        case '2':
        case '3':
            move_char( ch, path[pos] - '0', FALSE );
            break;

        case 'W':
            ch->position = POS_STANDING;
            act( "$n awakens and groans loudly.", ch, NULL, NULL, TO_ROOM );
            break;

        case 'S':
            ch->position = POS_SLEEPING;
            act( "$n lies down and falls asleep.", ch, NULL, NULL, TO_ROOM );
            break;

        case 'a':
            act( "$n says 'Hello Honey!'", ch, NULL, NULL, TO_ROOM );
            break;

        case 'b':
            act( "$n says 'What a view!  I must do something about that dump!'",
                 ch, NULL, NULL, TO_ROOM );
            break;

        case 'c':
            act( "$n says 'Vandals!  Youngsters have no respect for anything!'",
                 ch, NULL, NULL, TO_ROOM );
            break;

        case 'd':
            act( "$n says 'Good day, citizens!'", ch, NULL, NULL, TO_ROOM );
            break;

        case 'e':
            act( "$n says 'I hereby declare the city of Midgaard open!'",
                 ch, NULL, NULL, TO_ROOM );
            break;

        case 'E':
            act( "$n says 'I hereby declare the city of Midgaard closed!'",
                 ch, NULL, NULL, TO_ROOM );
            break;

        case 'O':
/*        do_function(ch, &do_unlock, "gate" ); */
            do_function( ch, &do_open, "gate" );
            break;

        case 'C':
            do_function( ch, &do_close, "gate" );
/*        do_function(ch, &do_lock, "gate" ); */
            break;

        case '.':
            move = FALSE;
            break;
    }

    pos++;
    return FALSE;
}

bool spec_poison( CHAR_DATA *ch )
{
    CHAR_DATA              *victim;

    if ( ch->position != POS_FIGHTING
         || ( victim = ch->fighting ) == NULL || number_percent(  ) > 2 * ch->level )
        return FALSE;

    act( "You bite $N!", ch, NULL, victim, TO_CHAR );
    act( "$n bites $N!", ch, NULL, victim, TO_NOTVICT );
    act( "$n bites you!", ch, NULL, victim, TO_VICT );
    spell_poison( gsn_poison, ch->level, ch, victim, TARGET_CHAR );
    return TRUE;
}

bool spec_thief( CHAR_DATA *ch )
{
    CHAR_DATA              *victim;
    CHAR_DATA              *v_next;
    int                     gold,
                            silver;

    if ( ch->position != POS_STANDING )
        return FALSE;

    for ( victim = ch->in_room->people; victim != NULL; victim = v_next )
    {
        v_next = victim->next_in_room;

        if ( IS_NPC( victim )
             || victim->level >= LEVEL_IMMORTAL
             || number_bits( 5 ) != 0 || !can_see( ch, victim ) )
            continue;

        if ( IS_AWAKE( victim ) && number_range( 0, ch->level ) == 0 )
        {
            act( "You discover $n's hands in your wallet!", ch, NULL, victim, TO_VICT );
            act( "$N discovers $n's hands in $S wallet!", ch, NULL, victim, TO_NOTVICT );
            return TRUE;
        }
        else
        {
            gold = victim->gold * UMIN( number_range( 1, 20 ), ch->level / 2 ) / 100;
            gold = UMIN( gold, ch->level * ch->level * 10 );
            ch->gold += gold;
            victim->gold -= gold;
            silver = victim->silver * UMIN( number_range( 1, 20 ), ch->level / 2 ) / 100;
            silver = UMIN( silver, ch->level * ch->level * 25 );
            ch->silver += silver;
            victim->silver -= silver;
            return TRUE;
        }
    }

    return FALSE;
}

bool spec_nasty( CHAR_DATA *ch )
{
    CHAR_DATA              *victim,
                           *v_next;
    int                     gold;

    if ( !IS_AWAKE( ch ) )
    {
        return FALSE;
    }

    if ( ch->position != POS_FIGHTING )
    {
        for ( victim = ch->in_room->people; victim != NULL; victim = v_next )
        {
            v_next = victim->next_in_room;
            if ( !IS_NPC( victim )
                 && ( victim->level > ch->level ) && ( victim->level < ch->level + 10 ) )
            {
                do_function( ch, &do_backstab, victim->name );
                if ( ch->position != POS_FIGHTING )
                {
                    do_function( ch, &do_murder, victim->name );
                }

                /*
                 * should steal some coins right away? :) 
                 */
                return TRUE;
            }
        }
        return FALSE;                                  /* No one to attack */
    }

    /*
     * okay, we must be fighting.... steal some coins and flee 
     */
    if ( ( victim = ch->fighting ) == NULL )
        return FALSE;                                  /* let's be paranoid.... */

    switch ( number_bits( 2 ) )
    {
        case 0:
            act( "$n rips apart your coin purse, spilling your gold!",
                 ch, NULL, victim, TO_VICT );
            act( "You slash apart $N's coin purse and gather his gold.",
                 ch, NULL, victim, TO_CHAR );
            act( "$N's coin purse is ripped apart!", ch, NULL, victim, TO_NOTVICT );
            gold = victim->gold / 10;                  /* steal 10% of his gold */
            victim->gold -= gold;
            ch->gold += gold;
            return TRUE;

        case 1:
            do_function( ch, &do_flee, "" );
            return TRUE;

        default:
            return FALSE;
    }
}

bool spec_troll_member( CHAR_DATA *ch )
{
    CHAR_DATA              *vch,
                           *victim = NULL;
    int                     count = 0;
    const char             *message;

    if ( !IS_AWAKE( ch ) || IS_AFFECTED( ch, AFF_CALM ) || ch->in_room == NULL
         || IS_AFFECTED( ch, AFF_CHARM ) || ch->fighting != NULL )
        return FALSE;

    /*
     * find an ogre to beat up 
     */
    for ( vch = ch->in_room->people; vch != NULL; vch = vch->next_in_room )
    {
        if ( !IS_NPC( vch ) || ch == vch )
            continue;

        if ( vch->pIndexData->vnum == MOB_VNUM_PATROLMAN )
            return FALSE;

        if ( vch->pIndexData->group == GROUP_VNUM_OGRES
             && ch->level > vch->level - 2 && !is_safe( ch, vch ) )
        {
            if ( number_range( 0, count ) == 0 )
                victim = vch;

            count++;
        }
    }

    if ( victim == NULL )
        return FALSE;

    /*
     * say something, then raise hell 
     */
    switch ( number_range( 0, 6 ) )
    {
        default:
            message = NULL;
            break;
        case 0:
            message = "$n yells 'I've been looking for you, punk!'";
            break;
        case 1:
            message = "With a scream of rage, $n attacks $N.";
            break;
        case 2:
            message = "$n says 'What's slimy Ogre trash like you doing around here?'";
            break;
        case 3:
            message = "$n cracks his knuckles and says 'Do ya feel lucky?'";
            break;
        case 4:
            message = "$n says 'There's no cops to save you this time!'";
            break;
        case 5:
            message = "$n says 'Time to join your brother, spud.'";
            break;
        case 6:
            message = "$n says 'Let's rock.'";
            break;
    }

    if ( message != NULL )
        act( message, ch, NULL, victim, TO_ALL );
    multi_hit( ch, victim, TYPE_UNDEFINED );
    return TRUE;
}

bool spec_ogre_member( CHAR_DATA *ch )
{
    CHAR_DATA              *vch,
                           *victim = NULL;
    int                     count = 0;
    const char             *message;

    if ( !IS_AWAKE( ch ) || IS_AFFECTED( ch, AFF_CALM ) || ch->in_room == NULL
         || IS_AFFECTED( ch, AFF_CHARM ) || ch->fighting != NULL )
        return FALSE;

    /*
     * find an troll to beat up 
     */
    for ( vch = ch->in_room->people; vch != NULL; vch = vch->next_in_room )
    {
        if ( !IS_NPC( vch ) || ch == vch )
            continue;

        if ( vch->pIndexData->vnum == MOB_VNUM_PATROLMAN )
            return FALSE;

        if ( vch->pIndexData->group == GROUP_VNUM_TROLLS
             && ch->level > vch->level - 2 && !is_safe( ch, vch ) )
        {
            if ( number_range( 0, count ) == 0 )
                victim = vch;

            count++;
        }
    }

    if ( victim == NULL )
        return FALSE;

    /*
     * say something, then raise hell 
     */
    switch ( number_range( 0, 6 ) )
    {
        default:
            message = NULL;
            break;
        case 0:
            message = "$n yells 'I've been looking for you, punk!'";
            break;
        case 1:
            message = "With a scream of rage, $n attacks $N.'";
            break;
        case 2:
            message = "$n says 'What's Troll filth like you doing around here?'";
            break;
        case 3:
            message = "$n cracks his knuckles and says 'Do ya feel lucky?'";
            break;
        case 4:
            message = "$n says 'There's no cops to save you this time!'";
            break;
        case 5:
            message = "$n says 'Time to join your brother, spud.'";
            break;
        case 6:
            message = "$n says 'Let's rock.'";
            break;
    }

    if ( message != NULL )
        act( message, ch, NULL, victim, TO_ALL );
    multi_hit( ch, victim, TYPE_UNDEFINED );
    return TRUE;
}

bool spec_patrolman( CHAR_DATA *ch )
{
    CHAR_DATA              *vch,
                           *victim = NULL;
    OBJ_DATA               *obj;
    const char             *message;
    int                     count = 0;

    if ( !IS_AWAKE( ch ) || IS_AFFECTED( ch, AFF_CALM ) || ch->in_room == NULL
         || IS_AFFECTED( ch, AFF_CHARM ) || ch->fighting != NULL )
        return FALSE;

    /*
     * look for a fight in the room 
     */
    for ( vch = ch->in_room->people; vch != NULL; vch = vch->next_in_room )
    {
        if ( vch == ch )
            continue;

        if ( vch->fighting != NULL )                   /* break it up! */
        {
            if ( number_range( 0, count ) == 0 )
                victim = ( vch->level > vch->fighting->level ) ? vch : vch->fighting;
            count++;
        }
    }

    if ( victim == NULL || ( IS_NPC( victim ) && victim->spec_fun == ch->spec_fun ) )
        return FALSE;

    if ( ( ( obj = get_eq_char( ch, WEAR_NECK_1 ) ) != NULL
           && obj->pIndexData->vnum == OBJ_VNUM_WHISTLE )
         || ( ( obj = get_eq_char( ch, WEAR_NECK_2 ) ) != NULL
              && obj->pIndexData->vnum == OBJ_VNUM_WHISTLE ) )
    {
        act( "You blow down hard on $p.", ch, obj, NULL, TO_CHAR );
        act( "$n blows on $p, ***WHEEEEEEEEEEEET***", ch, obj, NULL, TO_ROOM );

        for ( vch = char_list; vch != NULL; vch = vch->next )
        {
            if ( vch->in_room == NULL )
                continue;

            if ( vch->in_room != ch->in_room && vch->in_room->area == ch->in_room->area )
                send_to_char( "You hear a shrill whistling sound.\n\r", vch );
        }
    }

    switch ( number_range( 0, 6 ) )
    {
        default:
            message = NULL;
            break;
        case 0:
            message = "$n yells 'All roit! All roit! break it up!'";
            break;
        case 1:
            message = "$n says 'Society's to blame, but what's a bloke to do?'";
            break;
        case 2:
            message = "$n mumbles 'bloody kids will be the death of us all.'";
            break;
        case 3:
            message = "$n shouts 'Stop that! Stop that!' and attacks.";
            break;
        case 4:
            message = "$n pulls out his billy and goes to work.";
            break;
        case 5:
            message = "$n sighs in resignation and proceeds to break up the fight.";
            break;
        case 6:
            message = "$n says 'Settle down, you hooligans!'";
            break;
    }

    if ( message != NULL )
        act( message, ch, NULL, NULL, TO_ALL );

    multi_hit( ch, victim, TYPE_UNDEFINED );

    return TRUE;
}