/*
 * FILE:        ai.c
 * Purpose:     To create a basic AI sub-system to make
 *              AI battles more realistic..
 * Credits:     Zachery Crosswhite (Cyhawk/Thri)
 * Contact:     cyhawkx@gmail.com
 * Contact2:    AIM: CalibanL
 *
 * Install      Very basic, I used rom's previous ACT_MAGE
 * and info:    bit for this. Near the top of mobile_update in
 *              update.c, after the check for specs, and after
 *              my forced do_wear(ch, "all") (cough) I stuck..
 *
 *              if (IS_SET(ch->act, ACT_MAGE))
 *                  ai_wizard(ch);
 *
 *              I also changed MOBILE_PULSE to 20, due to
 *              the fact its normaly 8, and 99% of stock spells
 *              are 12 or higher.. so.. This is to prevent
 *              un-human-like casting ability. I would
 *              say make it 12 perhaps..
 *
 *              Idealy, when this is expanded, there will be
 *              a wrapper to determine class/AI, as well as
 *              choices for universal stuff (returning home,
 *              sleeping, eating, etc) before the specifics...
 *              As well as checks for how much 'wait' the mob
 *              has from spells (I have timed casting, but the
 *              idea is the same for Insta-cast/wait stock) This
 *              way, the mob casts as fast, or faster than a 
 *              good player.
 *
 * TODO:        Well, obviously this is just a basic piece of code...
 *              There needs to be ones for each 'class' of mobile
 *              you want to have, the 4 basics included in stock,
 *              as well as other special ones, (Group Healers,
 *              mobile leaders/'boss' mobs, maybe special mobs in
 *              here too, or 'super' mages..
 *
 *              Intelligence/Wisdom Checks, dumb wizards wont always
 *              cast, this also gives credence to giving mobs stat points
 *              too.
 *
 *              Checks for Mana, right now, they will always attempt to cast,
 *              I forsee some major spam-fests whe theyre out of mana.. ugh
 *              Maybe if theyre really hurt, they flee/find a healer..
 *              Ooo, I can see some intersting City AI here.
 *
 *              Debuf/Curse spells seprated. Lets face it, in PK, it goes in
 *              a specific order. Debuf->curse->fight (curse being any stat
 *              reduction or offensive enchantments). Mobiles should do this
 *              too.
 *
 *              Add function wrappers for ease of expandability and improvement
 *              of universal AI functions like going home, eating, etc.
 *
 * Licence:     This code falls under the Rom Licence, with the following
 *              additions:
 *              8) A helpfile accessable via the keyword '_thri' Must be
 *              viewable to all players, regardless of level, with the following
 *              Information:
 *              
 *              AI Sub-system created by Thri of Vandagard MUD.
 *              Email: cyhawkx@gmail.com
 *              AIM: CalibanL
 *
 *              Any previous snippets of mine used must also be included
 *              in this helpfile.
 *
 *              9) If you have a 'credits' command or credits helpfile,
 *              a small mention of my Name and the code snip should also
 *              be there.
 *
 *              10) You are also required to make contact with Thri via
 *              any means listed. (AIM/Email) Telling him you are going
 *              to use his code. (Email is best).
 *
 * Afterward:   Anywho, Enjoy the code, and make sure you expand it!
 *              Also, if you do expand it, send me what ya did, id love
 *              to incorporate this into the main sources, and maybe
 *              release a new stock codebase based around this, ROM 3.0?
 *              well, you know what i mean =)
 *
 *  -Cyhawk/Thri
 */

#if defined(macintosh)
#include <types.h>
#else
#include <sys/types.h>
#endif
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "merc.h"
#include "interp.h"
#include "magic.h"
#include "recycle.h"
#include "tables.h"

/**
 * AI_DEBUG: Turn on/off Chatting of what the AI is doing
 */
#define AI_DEBUG 1

/** ai()
 *
 * Primary wrapper to take care of basics (sleep, eat)
 * and to determine individual ai functions via class/race
 *
 *  Sleeping?   -> Hungry?  -> Thirsty?     -> Tired?
 *              -> Enemies? -> Attack?      -> race_ai
 *              -> class_ai
 *
 */
void ai(CHAR_DATA * mob)
{
    int _mob_hp             = 100 * (mob->hit) / (mob->max_hit + 1);
    bool can_heal_self      = TRUE; /* Expand me */
    CHAR_DATA * vch;
    CHAR_DATA * vch_next;
    
    /*
     * ACTION_ Sleeping?
     */
    switch (mob->position)
    {   default:
        /* STANDING: Do nothing */
        case POS_STANDING:
            break;
        /* SLEEPING: Check HP (75%) Day/Night, AFF_SLEEP */
        case POS_SLEEPING:
        case POS_RESTING:
        case POS_SITTING:
            /* AFF_SLEEP: Continue */
            if (IS_AFFECTED(mob, AFF_SLEEP))
                break;
            /* Hurt? Continue.. */
            if (_mob_hp <= 75 && !can_heal_self)
                break;
            break;
    }
    
    
    
    /*
     * ACTION_ Wear equipment
     */
    do_wear(mob, "all");
    
    /*
     * ACTION_ Class AI
     */
    if (ai_wizard(mob))
        return;
    if (ai_priest(mob))
        return;

    /*
     * ACTION_ Enemy here?
     */
    /* We don't want this called if the area is not empty
     * This does effectivly remove the possibility
     * of AI/AI combat in the background.
     *
     * I Placed most of the checks BEFORE we cycle through
     * the room list to hopefuly reduce CPU load.
     */
    /* Empty area check */
    if (mob->in_room->area->empty)
        return;
    /* Calm Check */
    if (IS_AFFECTED (mob, AFF_CALM))
        return;
    /* Safe room Check */
    if (IS_SET (mob->in_room->room_flags, ROOM_SAFE))
        return;
    /* Aggressive Check */
    if (!IS_SET (mob->act, ACT_AGGRESSIVE))
        return;
    /* Awake Check */
    if (!IS_AWAKE(mob))
        return;
    /* Fighting Check */
    if (mob->fighting != NULL)
        return;
    
    /* Not empty, ok, search for a victim */
    for (vch = mob->in_room->people; vch != NULL; vch = vch_next)
    {
        vch_next = vch->next_in_room;
        
        /* Make sure its a PC, and can see the victim */
        if (IS_NPC (vch))
            continue;
        
        /* Wizinvis immortals immune to aggri mobs */
        if (vch->invis_level >= 106)
        {
#if AI_DEBUG
            do_function(mob, &do_say, "Can't attack imms!");
#endif
            return;
        }
        
        /* Ok, lets attack em, and have some fun too */
        if (IS_AFFECTED(vch, AFF_INVISIBLE))
        {
            do_function(mob, &do_emote, "looks at you.");
            do_function(mob, &do_say, "I found you!");
            multi_hit (mob, vch, TYPE_UNDEFINED);
            return;
        }
        if (IS_AFFECTED(vch, AFF_HIDE))
        {
            do_function(mob, &do_emote, "sniffs around...");
            do_function(mob, &do_say, "Trying to hide are we?");
            multi_hit (mob, vch, TYPE_UNDEFINED);
            return;
        }
        if (IS_AFFECTED(vch, AFF_SNEAK))
        {
            do_function(mob, &do_emote, "laughs.");
            do_function(mob, &do_say, "Sneaky one aren't ya...");
            multi_hit (mob, vch, TYPE_UNDEFINED);
            return;
        }
        /* Ok, not affected by anything, just kill em! */
        multi_hit (mob, vch, TYPE_UNDEFINED);
        return;
    }
    
    return;
}

/* Basic Action outline:
                    -> Cast
        -> Fight    -> Melee
Action              -> Debuf
                    -> Flee?

                    -> Heal Self
        -> Idle     -> Enchant Self
                    -> Idle Chit Chat
*/

/** ai_wizard()
 *
 * Primary AI Control for Wizard mobs, follows
 * a basic procedure, hopefuly this will work.
 *
 * Returns TRUE if it did something.
 */
bool ai_wizard(CHAR_DATA * mob) {
    int _mob_hp = 100 * (mob->hit) / (mob->max_hit + 1);
    int _level  = mob->level;
    int chance  = 0;
    
    /* If not a mage, dont go */
    if (!IS_SET(mob->act, ACT_MAGE))
        return FALSE;
    
    mob->spell_cyan     = 50;
    mob->spell_red      = 50;
    mob->spell_yellow   = 50;
    
    /* Ok, Lets start with idle actions.. */
    if (mob->fighting == NULL)
    {
        /* ACTION_ Heal Start (75% or lower) */
        if (_mob_hp < 75)
        {
#if AI_DEBUG
            do_function (mob, &do_say, "I need to heal myself");
#endif
            /* ACTION_ Heal: Lets see if he can hide first */
            if (!is_affected (mob, skill_lookup("invisibility"))
                && _level >= 8
                && !IS_AFFECTED(mob, AFF_INVISIBLE))
            {
#if AI_DEBUG
                do_function (mob, &do_say, "Woha that was close, gotta hide!");
#endif
                do_function (mob, &do_cast, "'invisibility'");
                return TRUE;
            }
            
            if (_level >= 25)
            {
#if AI_DEBUG
                do_function (mob, &do_say, "I'm over level 25, I'll use Heal");
#endif
                do_function (mob, &do_cast, "heal self");
                return TRUE;
            }
            else
            {
                if (mob->position == POS_RESTING)
                    return TRUE;
#if AI_DEBUG
                do_function (mob, &do_say, "I'm not over level 25, I need to rest");
#endif
                do_function (mob, &do_rest, "rest");
                return TRUE;
            }
        }
        else
        {
            if (mob->position != POS_STANDING)
            {
#if AI_DEBUG
                do_function (mob, &do_say, "Much Better!");
#endif
                do_function (mob, &do_stand, "");
            }
        }
                
        /* ACTION_ Heal End */
        
        /* ACTION_ Enchant */
        /* Enchant - Orcish Strength */
        if (!is_affected (mob, skill_lookup("orcish strength"))
           && _level >= 3)
        {
#if AI_DEBUG
            do_function (mob, &do_say, "I don't have orcish strength on.");
#endif
            do_function (mob, &do_cast, "'orcish strength'");
            return TRUE;
        }
        /* Enchant - Invisibility */
        if (!is_affected (mob, skill_lookup("invisibility"))
            && _level >= 8
            && !IS_AFFECTED(mob, AFF_INVISIBLE)
            && mob->fighting == NULL)
        {
#if AI_DEBUG
            do_function (mob, &do_say, "I need to be invisible!");
#endif
            do_function (mob, &do_cast, "'invisibility'");
            return TRUE;
        }
        /* Enchant - Invisibility */
        if (!is_affected (mob, skill_lookup("stone skin"))
            && _level >= 12)
        {
#if AI_DEBUG
            do_function (mob, &do_say, "I need to protect myself with Stone skin!");
#endif
            do_function (mob, &do_cast, "'stone skin'");
            return TRUE;
        }
        /* Enchant - Adept */
        if (!is_affected (mob, skill_lookup("adept"))
            && _level >= 20)
        {
#if AI_DEBUG
            do_function (mob, &do_say, "What kind of Wizard isnt adept?");
#endif
            do_function (mob, &do_cast, "'adept'");
            return TRUE;
        }
        /* Enchant - Flight */
        if (!is_affected (mob, skill_lookup("flight"))
            && _level >= 23
            && !IS_AFFECTED(mob, AFF_FLYING))
        {
#if AI_DEBUG
            do_function (mob, &do_say, "My legs are tired...");
#endif
            do_function (mob, &do_cast, "'flight'");
            return TRUE;
        }
        /* Enchant - Giant Strength */
        if (!is_affected (mob, skill_lookup("giant strength"))
            && _level >= 26)
        {
#if AI_DEBUG
            do_function (mob, &do_emote, "looks in a mirror and tries to flex.");
            do_function (mob, &do_say, "Hmm, the ladies sure dont like wimps!");
#endif
            do_function (mob, &do_cast, "'giant strength'");
            return TRUE;
        }
        /* Enchant - Haste */
        if (!is_affected (mob, skill_lookup("haste"))
            && _level >= 30)
        {
#if AI_DEBUG
            do_function (mob, &do_say, "I feel sluggish...");
#endif
            do_function (mob, &do_cast, "'haste'");
            return TRUE;
        }
        /* Enchant - Immolate */
        if (!is_affected (mob, skill_lookup("immolate"))
            && _level >= 31)
        {
#if AI_DEBUG
            do_function (mob, &do_say, "I need to feel some POWAAAHH!!");
#endif
            do_function (mob, &do_cast, "'immolate'");
            return TRUE;
        }
        /* Enchant - Anti-magic Shell */
        if (!is_affected (mob, skill_lookup("anti-magic shell"))
            && _level >= 35)
        {
#if AI_DEBUG
            do_function (mob, &do_say, "I feel too vulnerable..");
#endif
            do_function (mob, &do_cast, "'anti-magic shell'");
            return TRUE;
        }
        /* Enchant - Manashield */
        if (!is_affected (mob, skill_lookup("manashield"))
            && _level >= 45)
        {
#if AI_DEBUG
            do_function (mob, &do_say, "Dude? Wheres my Manashield?");
#endif
            do_function (mob, &do_cast, "'manashield'");
            return TRUE;
        }
    }
    
    /* Ok, Idle actions done, lets start the fighting stuff! */
    if (mob->fighting != NULL)
    {
        /* ACTION_ Fighting: Flee under 35% */
        if (_mob_hp <= 35)
        {
            /* 50% chance to flee every update */
            if (number_range(1, 100) > 50)
            {
#if AI_DEBUG
                do_function (mob, &do_say, "Holy bejezzus batman, Im out of here!");
#endif
                do_function (mob, &do_flee, "");
                return TRUE;
            }
        }
        /* Idealy, spells will be Highest Level to Lowest
         * So the AI will hit harder as theyre stronger..
         */
        /* Cut levels every 20 for ease of use */
        
        /*
         * Level 21-41
         */
        if ((_level >= 21) && (_level <= 999))
        {
            chance = number_range(1, 7);
            
            if (chance == 1 && _level >= 21)
            {
#if AI_DEBUG
                do_function (mob, &do_say, "Acid anyone?");
#endif
                do_function (mob, &do_cast, "'acid blast'");
                return TRUE;
            }
            if (chance == 2 && _level >= 28)
            {
#if AI_DEBUG
                do_function (mob, &do_say, "Mmm fire..");
#endif
                do_function (mob, &do_cast, "'fire storm'");
                return TRUE;
            }
            if (chance == 3 && _level >= 28)
            {
#if AI_DEBUG
                do_function (mob, &do_say, "Just die..");
#endif
                do_function (mob, &do_cast, "'death touch'");
                return TRUE;
            }
            if (chance == 4 && _level >= 32)
            {
#if AI_DEBUG
                do_function (mob, &do_say, "Here kitty kitty..");
#endif
                do_function (mob, &do_cast, "'energy drain'");
                return TRUE;
            }
            if (chance == 5 && _level >= 36)
            {
#if AI_DEBUG
                do_function (mob, &do_say, "Is it cold in here, or is it just you?");
#endif
                do_function (mob, &do_cast, "'ice bolt'");
                return TRUE;
            }
            if (chance == 6 && _level >= 38)
            {
#if AI_DEBUG
                do_function (mob, &do_say, "crush crush CRUSH CRUSH! FIRE!");
#endif
                do_function (mob, &do_cast, "'fire crush'");
                return TRUE;
            }
            if (chance == 7 && _level >= 40)
            {
#if AI_DEBUG
                do_function (mob, &do_say, "Wanna smell my breath?");
#endif
                do_function (mob, &do_cast, "'acid breath'");
                return TRUE;
            }
            return TRUE;
        }
        
        /*
         * Level Under 20
         */
        if (_level <= 20)
        {
            chance = number_range(1, 10);

            if (chance == 1 && _level >= 1)
            {
#if AI_DEBUG
                do_function (mob, &do_say, "Eat Wizard Fire!");
#endif
                do_function (mob, &do_cast, "'wizard fire'");
                return TRUE;
            }
            if (chance == 2 && _level >= 1)
            {
#if AI_DEBUG
                do_function (mob, &do_say, "Suck on some Wizard Frost!");
#endif
                do_function (mob, &do_cast, "'wizard frost'");
                return TRUE;
            }
            if (chance == 3 && _level >= 1)
            {
#if AI_DEBUG
                do_function (mob, &do_say, "Feel the Powah of Wizard Spark!");
#endif
                do_function (mob, &do_cast, "'wizard spark'");
                return TRUE;
            }
            if (chance == 4 && _level >= 4)
            {
#if AI_DEBUG
                do_function (mob, &do_say, "Are my hands hot to you?");
#endif
                do_function (mob, &do_cast, "'burning hands'");
                return TRUE;
            }
            if (chance == 5 && _level >= 5)
            {
#if AI_DEBUG
                do_function (mob, &do_say, "Do you feel sick?");
#endif
                do_function (mob, &do_cast, "'weaken'");
                return TRUE;
            }
            if (chance == 6 && _level >= 6)
            {
#if AI_DEBUG
                do_function (mob, &do_say, "Ooo, purtty colours...");
#endif
                do_function (mob, &do_cast, "'colour spray'");
                return TRUE;
            }
            if (chance == 7 && _level >= 9)
            {
#if AI_DEBUG
                do_function (mob, &do_say, "Ooo, its chilly in here!");
#endif
                do_function (mob, &do_cast, "'chill touch'");
                return TRUE;
            }
            if (chance == 8 && _level >= 10)
            {
#if AI_DEBUG
                do_function (mob, &do_say, "Wow, your strong, can I have some?");
#endif
                do_function (mob, &do_cast, "'leech strength'");
                return TRUE;
            }
            if (chance == 9 && _level >= 16)
            {
#if AI_DEBUG
                do_function (mob, &do_say, "You can't hit what you can't see!");
#endif
                do_function (mob, &do_cast, "'blindness'");
                return TRUE;
            }
            if (chance == 10 && _level >= 19)
            {
#if AI_DEBUG
                do_function (mob, &do_say, "Hope this hurts!");
#endif
                do_function (mob, &do_cast, "'force spike'");
                return TRUE;
            }
            return TRUE;
        }
    }   /* End fighting */
    
    return FALSE;
}

/** ai_priest()
 *
 * Primary AI Control for Priest Mobs.
 *
 * Returns TRUE if it did something.
 */
bool ai_priest(CHAR_DATA * mob) {
    int _mob_hp = 100 * (mob->hit) / (mob->max_hit + 1);
    int _level  = mob->level;
    int chance  = 0;
    
    /* If not a mage, dont go */
    if (!IS_SET(mob->act, ACT_CLERIC))
        return FALSE;

    mob->spell_cyan     = 50;
    mob->spell_yellow   = 50;
    mob->spell_red      = 50;
    mob->spell_violet   = 50;
    /*
     * ACTION_ Not fighting
     */
    if (mob->fighting == NULL)
    {
        /*
         * ACTION_ Remove Bad crap
         */
        {
        /* Remove: Poison */
        if (IS_AFFECTED(mob, AFF_POISON)
           && _level >= 14)
        {
#if AI_DEBUG
            do_function (mob, &do_say, "I feel so.. sick...");
#endif
            do_function (mob, &do_cast, "'cure poison'");
            return TRUE;
        }
        /* Remove: Blindness */
        if (IS_AFFECTED(mob, AFF_BLIND)
           && _level >= 11)
        {
#if AI_DEBUG
            do_function (mob, &do_say, "I can't see!!!");
#endif
            do_function (mob, &do_cast, "'cure blindness'");
            return TRUE;
        }
        /* Remove: Curse */
        if (IS_AFFECTED(mob, AFF_CURSE)
           && _level >= 21)
        {
#if AI_DEBUG
            do_function (mob, &do_say, "I feel disconnected..");
#endif
            do_function (mob, &do_cast, "'remove curse' self");
            return TRUE;
        }
        }
    
        /* ACTION_ Heal Start (75% or lower) */
        if (_mob_hp < 75)
        {
            /* Work backwards in terms of power for better heal spells */
            /* HEAL_ Restoration */
            if (_level >= 95)
            {
#if AI_DEBUG
                do_function (mob, &do_say, "Restoration here I come!");
#endif
                do_function (mob, &do_cast, "restoration");
                return TRUE;
            }
            /* HEAL_ Heal Spell */
            if (_level >= 60)
            {
#if AI_DEBUG
                do_function (mob, &do_say, "I'll just use HEAL then...");
#endif
                do_function (mob, &do_cast, "heal");
                return TRUE;
            }
            /* HEAL_ Cure Critical */
            if (_level >= 25)
            {
#if AI_DEBUG
                do_function (mob, &do_say, "Being beat on is the suck");
#endif
                do_function (mob, &do_cast, "cure serious");
                return TRUE;
            }
            /* HEAL_ Heal Spell */
            if (_level >= 60)
            {
#if AI_DEBUG
                do_function (mob, &do_say, "I'll just use HEAL then...");
#endif
                do_function (mob, &do_cast, "heal");
                return TRUE;
            }
            if (_level >= 1)
            {
#if AI_DEBUG
                do_function (mob, &do_say, "I'm at least level 1, cure light wounds");
#endif
                do_function (mob, &do_cast, "cure light wounds");
                return TRUE;
            }
        }
        /*
         * ACTION_ Healing: End
         */
        /*
         * ACTION_ Enchant
         */
        {
        /* Enchant - Armor */
        if (!is_affected (mob, skill_lookup("armor"))
           && _level >= 1)
        {
#if AI_DEBUG
            do_function (mob, &do_say, "I don't have armor on...");
#endif
            do_function (mob, &do_cast, "'armor'");
            return TRUE;
        }
        /* Enchant - Bless */
        if (!is_affected (mob, skill_lookup("bless"))
           && _level >= 3)
        {
#if AI_DEBUG
            do_function (mob, &do_say, "I'm not rightous!");
#endif
            do_function (mob, &do_cast, "'bless'");
            return TRUE;
        }
        /* Enchant - Bark Skin */
        if (!is_affected (mob, skill_lookup("bark skin"))
           && _level >= 15)
        {
#if AI_DEBUG
            do_function (mob, &do_say, "My Skin is too soft");
#endif
            do_function (mob, &do_cast, "'bark skin'");
            return TRUE;
        }
        /* Enchant - Holy Sight */
        if (!is_affected (mob, skill_lookup("holy sight"))
           && _level >= 37)
        {
#if AI_DEBUG
            do_function (mob, &do_say, "My My its dark in here");
#endif
            do_function (mob, &do_cast, "'holy sight'");
            return TRUE;
        }
        /* Enchant - Frenzy */
        if (!is_affected (mob, skill_lookup("frenzy"))
           && _level >= 41)
        {
#if AI_DEBUG
            do_function (mob, &do_say, "I'm not mad enough!");
#endif
            do_function (mob, &do_cast, "'frenzy'");
            return TRUE;
        }
        /* Enchant - Sanctuary */
        if (!is_affected (mob, skill_lookup("sanctuary"))
           && !IS_AFFECTED(mob, AFF_SANCTUARY)
           && _level >= 46)
        {
#if AI_DEBUG
            do_function (mob, &do_say, "Wow, its not pretty here");
#endif
            do_function (mob, &do_cast, "'sanctuary'");
            return TRUE;
        }
        }

    }
    /*
     * END Not Fighting
     */
    return FALSE;
}