/*
 *  Installation instructions:  
 *    comment out or delete the old spell_ventriloquate and replace
 *    it with the following.
 *  This was one of my very early snippets and I hope you enjoy it! :)
 *        -Valcados
 */


char *  drunk_speech    args( ( const char *argument, CHAR_DATA *ch ) );

ch_ret spell_ventriloquate( int sn, int level, CHAR_DATA *ch, void *vo )
{
    char speaker[MAX_INPUT_LENGTH], *speech;
    char buf1[MAX_STRING_LENGTH];
    char buf2[MAX_STRING_LENGTH];
    CHAR_DATA *vch;
    CHAR_DATA *victim;
    char *verb_say = "say", *verb_says = "says";
    bool victim_saved = FALSE;
    OBJ_DATA *obj = NULL;

    char * const npc_race_verbs[] = {
       "ant", "hiss", "hisses",              "ape", "howl", "howls",
       "baboon", "howl", "howls",            "bat", "screech", "screeches",
       "bear", "roar", "roars",              "bee", "buzz", "buzzes",
       "beetle", "click", "clicks",          "boar", "squeal", "squeals",
       "bugbear", "growl", "growls",         "cat", "meow", "meows",
       "dwarf", "grunt", "grunts",           "drow", "hiss", "hisses",
       "dog", "bark", "barks",               "dragon", "loudly roar", "loudly roars",
       "fly", "buzz", "buzzes",              "gelatin", "ooze", "oozes",
       "ghoul", "shriek", "shrieks",         "golem", "rumble", "rumbles",
       "half-ogre", "grunt", "grunts",       "half-orc", "grunt", "grunts",
       "half-troll", "roar", "roars",        "harpy", "sing", "sings",
       "hobgoblin", "mutter", "mutters",     "kobold", "whine", "whines",
       "lizardman", "hiss", "hisses",        "locust", "buzz", "buzzes",
       "minotaur", "grunt", "grunts",        "mule", "hee-haw", "hee-haws",
       "neanderthall", "stammer", "stammers","ooze", "ooze", "oozes",
       "pixie", "squeal", "squeals",         "rat", "squeak", "squeaks",
       "shadow", "whisper", "whispers",      "shrieker", "shriek", "shrieks",
       "skeleton", "groan", "groans",        "troll", "roar", "roars",
       "vampire", "hiss", "hisses",          "slime", "gurgle", "gurgles",
       "snake", "hiss", "hisses",            "undead", "moan", "moans",
       "wight", "wail", "wails",             "wolf", "howl", "howls",
       "zombie", "moan", "quietly moans",    "bovine", "moo", "moos",
       "canine", "bark", "barks",            "feline", "meow", "meows",
       "rodent", "squeak", "squeaks",        "reptile", "hiss", "hisses",
       "amphibian", "hiss", "hisses",        "fish", "gurgle", "gurgles",
       "crustacean", "gurgle", "gurgles",    "insect", "buzz", "buzzes",
       "spirit", "hum", "hums",              "horse", "whinny", "whinnies",
       "end" };

    char * const npc_class_verbs[] = {
       "vampire", "hiss", "hisses",          "nephandi", "cackle", "cackles",
       "savage", "howl", "howls",            "pirate", "growl", "growls",
       "mayor", "proclaim", "proclaims",     "king", "proclaim", "proclaims",
       "queen", "proclaim", "proclaims",     "end" };


                      
    target_name = one_argument( target_name, speaker );
    speech = target_name;

    /* Check if the victim is a character (PC or NPC) */
    victim = get_char_room( ch, speaker );

    if ( victim )
    {
      if ( victim == ch )
      {
        send_to_char( "Don't even bother.\n\r", ch );
        return rSPELL_FAILED;
      }

      /* If it's an NPC, use a special race-based verb instead of "say" */
      if ( IS_NPC( victim ) )
      {
        int race;
        char *racename = get_race(victim);
        char *classname = get_class(victim);

        for ( race = 0; str_cmp( npc_race_verbs[race], "end" ); race += 3 )
        {
          if ( !str_cmp( npc_race_verbs[race], racename ) )
          {
            verb_say = npc_race_verbs[race + 1];
            verb_says = npc_race_verbs[race + 2];
            break;
          }
        }

        /* If the race is normally "say", see if the class gets a special verb */

        if ( !str_cmp( verb_say, "say" ) )
        {
          int class;

          for ( class = 0; str_cmp( npc_class_verbs[class], "end" ); class += 3 )
          {
            if ( !str_cmp( npc_class_verbs[class], classname ) )
            {
              verb_say = npc_class_verbs[class + 1];
              verb_says = npc_class_verbs[class + 2];
              break;
            }
          }
        }
      }  /* if IS_NPC */

      /* If there is an actual victim, they get to attempt a save vs. spell */

      victim_saved = ( saves_spell_staff( level, victim ) ? TRUE : FALSE );

      if ( IS_NPC( victim ) )
      sprintf( speaker, victim->short_descr );
      else
      sprintf( speaker, victim->name );
      *speaker = UPPER( *speaker );
    }

    /* If no victim was found, check if an item was found */

    if ( !victim 
    && ( ( obj = get_obj_list( ch, speaker, ch->in_room->first_content ) ) != NULL ) )
    {
      /* If an object is found, then mentalstate applies */
      if ( ms_find_obj( ch ) )
      return rSPELL_FAILED;

      sprintf( speaker, obj->short_descr );
      *speaker = UPPER( *speaker );

      /* If it is an object, switch on its type */
      switch ( obj->item_type )
      {
        case ITEM_WAND:
        case ITEM_RUNE:
        case ITEM_PORTAL:
        if ( IS_OBJ_STAT( obj, ITEM_MAGIC ) )
        verb_say = "hum", verb_says = "hums";
        break;

        case ITEM_POTION:
        case ITEM_DRINK_CON:
        case ITEM_BLOOD:
        case ITEM_FOUNTAIN:
        if ( obj->item_type != ITEM_DRINK_CON || obj->value[0] > 0 )
        {
          if ( number_bits(1) )
          verb_say = "gurgle", verb_says = "gurgles";
          else
          verb_say = "bubble", verb_says = "bubbles";
        }
        break;

        case ITEM_KEYRING:
        if ( obj->first_content )
        verb_say = "jingle", verb_says = "jingles";
        break;

        case ITEM_FIRE:
        verb_say = "crackle", verb_says = "crackles";
        break;

        default:
        /* Handle some special cases */
        if ( obj->pIndexData->vnum == OBJ_VNUM_MONEY_SOME )
        verb_say = "jingle", verb_says = "jingles";
        else if ( obj->pIndexData->vnum == OBJ_VNUM_CORPSE_NPC
             ||   obj->pIndexData->vnum == OBJ_VNUM_CORPSE_PC )
        verb_say = "moan", verb_says = "moans";
        else if ( obj->pIndexData->vnum == OBJ_VNUM_SEVERED_HEAD )
        verb_say = "rasp", verb_says = "rasps";
        else if ( obj->pIndexData->vnum == OBJ_VNUM_TORN_HEART )
        verb_say = "thump", verb_says = "thumps";
        break;
      }
    }

    /* Handle some common special cases if neither victim
     * nor object were found */

    if ( !victim && !obj )
    {
      if ( !str_cmp( speaker, "wall" ) )
      {
        sprintf( speaker, "a nearby wall" );
        verb_say = "rumble", verb_says = "rumbles";
      }
      else if ( !str_cmp( speaker, "floor" )
           ||   !str_cmp( speaker, "ground" )
           ||   !str_cmp( speaker, "roof" )
           ||   !str_cmp( speaker, "ceiling" ) )
      verb_say = "loudly rumble", "loudly rumbles";
      else if ( !str_cmp( speaker, "wind" ) )
      {
        if ( IS_SET( ch->in_room->room_flags, ROOM_INDOORS ) )
        sprintf( speaker, "the wind outside" );
        else
        sprintf( speaker, "the wind" );
        verb_say = "wail", verb_says = "wails";
      }
      else if ( is_name( speaker, "you me him her he she it they them" ) )
      {
        send_to_char( "Be more specific!\n\r", ch );
        return rSPELL_FAILED;
      }
    }

    /* Take care of some formalities */
    if ( !IS_NPC( ch ) && xIS_SET( ch->act, PLR_SILENCE ) )
    {
      send_to_char( "A godly force intercepts your voice.\n\r", ch );
      return rSPELL_FAILED;
    }
    if ( IS_SET( ch->in_room->room_flags, ROOM_SILENCE ) )
    {
      send_to_char( "Your impotent spell doesn't make a sound.\n\r", ch );
      return rSPELL_FAILED;
    }
    if ( victim && IS_IMMORTAL( victim )
    && ( IS_NPC( ch ) || get_trust( ch ) <= get_trust( victim ) ) )
    {
      ch_printf( ch, "In your sheer awe of %s, you can't bring yourself to do it.\n\r", victim->name );
      return rSPELL_FAILED;
    }
    
    while ( *speech == ' ' )
    speech++;
    
    if ( *speech == '\0' || ( *speech == '\'' && speech[1] == '\0' ) )
    {
      send_to_char( "What do you want to make them say?\n\r", ch );
      return rSPELL_FAILED;
    }

    speech = drunk_speech( speech, ch );

    /* Okay, let's finally get down to business! */

    if ( victim )
    {
      sprintf( buf1, " %s '%s'.\n\r", verb_says, speech );
      sprintf( buf2, " %s '%s'.\n\r", verb_say, speech );
      for ( vch = ch->in_room->first_person; vch; vch = vch->next_in_room )
      {
        if ( vch != victim )
        {
          set_char_color( AT_SAY, vch );
          if ( vch == ch )
          ch_printf( ch, "You make %s%s", PERS(victim, ch), buf2 );
          else if ( IS_IMMORTAL( vch ) )
          ch_printf( vch, "%s makes %s%s", PERS(ch, vch), PERS(victim, vch), buf2 );
          else if ( victim_saved || saves_spell_staff( level, vch ) )
          ch_printf( vch, "Someone makes %s%s", PERS(victim, vch), buf2 );
          else
          ch_printf( vch, "%s%s", can_see(vch,victim)? speaker : "Someone", buf1 );
        }
      }
      return rNONE;
    }

    if ( obj )
    {
      sprintf( buf1, " %s '%s'.\n\r", verb_says, speech );
      sprintf( buf2, " %s '%s'.\n\r", verb_say, speech );
      for ( vch = ch->in_room->first_person; vch; vch = vch->next_in_room )
      {
        set_char_color( AT_SAY, vch );
        if ( vch == ch )
        ch_printf( ch, "You make %s%s", obj->short_descr, buf2 );
        else if ( IS_IMMORTAL( vch ) )
        ch_printf( vch, "%s makes %s%s", PERS(ch, vch),
            can_see_obj( vch, obj ) ? obj->short_descr : "Something", buf2 );
        else if ( saves_spell_staff( level, vch ) )
        ch_printf( vch, "Someone makes %s%s",
            can_see_obj( vch, obj ) ? obj->short_descr : "Something", buf2 );
        else
        ch_printf( vch, "%s%s", can_see_obj( vch, obj ) ?
            speaker : "something", buf1 );
      }
      return rNONE;
    }

    /* At this point, the speaker is neither victim nor object */

    sprintf( buf1, "%s %s '%s'.\n\r", speaker, verb_says, speech );
    sprintf( buf2, "Someone makes %s %s '%s'.\n\r", speaker, verb_say, speech );
    *buf1 = UPPER(*buf1);
    for ( vch = ch->in_room->first_person; vch; vch = vch->next_in_room )
    {
      set_char_color( AT_SAY, vch );
      if ( vch == ch )
      ch_printf( ch, "You make %s %s '%s'.\n\r", speaker, verb_say, speech );
      else if ( IS_IMMORTAL( vch ) )
      ch_printf( vch, "%s makes %s %s '%s'.\n\r", PERS(ch,vch), speaker, verb_say, speech );
      else if ( saves_spell_staff( level, vch ) )
      send_to_char( buf2, vch );
      else
      send_to_char( buf1, vch );
    }

    return rNONE;
}