242 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			242 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
/*/////////////////////// [On Damaged] /////////////////////////////////////////
 | 
						|
    Filename: nw_c2_default6 or J_AI_OnDamaged
 | 
						|
///////////////////////// [On Damaged] /////////////////////////////////////////
 | 
						|
    We attack any damager if same area (and not already fighting
 | 
						|
    then search for enemies (defaults to searching if there are no enemies left).
 | 
						|
///////////////////////// [History] ////////////////////////////////////////////
 | 
						|
    1.3 - If we have a damager, not equal faction, and not a DM...
 | 
						|
            - We set Max Elemental damage.
 | 
						|
        - Sets the highest damager and amount (if the new damager is seen/heard)
 | 
						|
        - Polymorph improved a little
 | 
						|
        - Hide check
 | 
						|
        - Morale penalty (if set)
 | 
						|
    1.4 - Elemental damage fixed with bugfixed introduced in later patches.
 | 
						|
        - Moved things around, more documentation, a little more ordered.
 | 
						|
        - Added the missing silent shout strings to get allies to attack.
 | 
						|
        - Damaged taunting will not happen if we are dead.
 | 
						|
///////////////////////// [Workings] ///////////////////////////////////////////
 | 
						|
    Now with fixes, we can correctly set physical damage done (and elemental
 | 
						|
    damage).
 | 
						|
 | 
						|
    Otherwise, this acts like a hositile spell, or a normal attack or pickpocket
 | 
						|
    attempt would - and attack the damn person who dares damage us!
 | 
						|
///////////////////////// [Arguments] //////////////////////////////////////////
 | 
						|
    Arguments: GetTotalDamageDealt, GetLastDamager, GetCurrentHitPoints (and max),
 | 
						|
               GetDamageDealtByType (must be done seperatly for each, doesn't count melee damage)
 | 
						|
///////////////////////// [On Damaged] ///////////////////////////////////////*/
 | 
						|
 | 
						|
#include "J_INC_OTHER_AI"
 | 
						|
 | 
						|
void main()
 | 
						|
{
 | 
						|
    // Pre-damaged-event. Returns TRUE if we interrupt this script call.
 | 
						|
    if(FirePreUserEvent(AI_FLAG_UDE_DAMAGED_PRE_EVENT, EVENT_DAMAGED_PRE_EVENT)) return;
 | 
						|
 | 
						|
    // AI status check. Is the AI on?
 | 
						|
    if(GetAIOff()) return;
 | 
						|
 | 
						|
    // Define Objects/Integers.
 | 
						|
    int nDamage = GetTotalDamageDealt();
 | 
						|
    object oDamager = GetLastDamager();
 | 
						|
    // Check to see if we will polymorph.
 | 
						|
    int nPolymorph = GetAIConstant(AI_POLYMORPH_INTO);
 | 
						|
 | 
						|
    // Total up the physical damage
 | 
						|
 | 
						|
    // Polymorph check.
 | 
						|
    if(nPolymorph >= 0)
 | 
						|
    {
 | 
						|
        // We won't polymorph if already so
 | 
						|
        if(!GetHasEffect(EFFECT_TYPE_POLYMORPH))
 | 
						|
        {
 | 
						|
            // Polymorph into the requested shape. Cannot be dispelled.
 | 
						|
            effect eShape = SupernaturalEffect(EffectPolymorph(nPolymorph));
 | 
						|
            effect eVis = EffectVisualEffect(VFX_FNF_SUMMON_UNDEAD);
 | 
						|
            DelayCommand(1.0, ApplyEffectToObject(DURATION_TYPE_PERMANENT, eShape, OBJECT_SELF));
 | 
						|
            DelayCommand(1.0, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, OBJECT_SELF));
 | 
						|
        }
 | 
						|
        DeleteAIConstant(AI_POLYMORPH_INTO);// We set it to invalid (sets to 0).
 | 
						|
    }
 | 
						|
    // First, we check AOE spells...
 | 
						|
    if(GetObjectType(oDamager) == OBJECT_TYPE_AREA_OF_EFFECT)
 | 
						|
    {
 | 
						|
        // Set the damage done by it (the last damage)
 | 
						|
        // Set to the tag of the AOE, prefixed AI style to be sure.
 | 
						|
        // - Note, doesn't matter about things like
 | 
						|
        if(nDamage > 0)
 | 
						|
        {
 | 
						|
            // Set it to object to string, which we will delete later anywho.
 | 
						|
            SetAIInteger(ObjectToString(oDamager), nDamage);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    // Hostile attacker...but it doesn't matter (at the moment) if they even
 | 
						|
    // did damage.
 | 
						|
    // * GetIgnoreNoFriend() wrappers DM, Validity, Faction Equal and Dead checks in one
 | 
						|
    else if(!GetIgnoreNoFriend(oDamager))
 | 
						|
    {
 | 
						|
        // Adjust automatically if set. (and not an AOE)
 | 
						|
        if(GetSpawnInCondition(AI_FLAG_OTHER_CHANGE_FACTIONS_TO_HOSTILE_ON_ATTACK, AI_OTHER_MASTER))
 | 
						|
        {
 | 
						|
            if(!GetIsEnemy(oDamager) && !GetFactionEqual(oDamager))
 | 
						|
            {
 | 
						|
                AdjustReputation(oDamager, OBJECT_SELF, -100);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        // Turn of hiding, a timer to activate Hiding in the main file. This is
 | 
						|
        // done in each of the events, with the opposition checking seen/heard.
 | 
						|
        TurnOffHiding(oDamager);
 | 
						|
 | 
						|
        // Did they do damage to use? (IE: No DR) Some things are inapproprate
 | 
						|
        // to check if no damage was actually done.
 | 
						|
        if(nDamage > 0)
 | 
						|
        {
 | 
						|
            // Speak the damaged string, if applicable.
 | 
						|
            // * Don't speak when dead. 1.4 change (an obvious one to make)
 | 
						|
            if(CanSpeak())
 | 
						|
            {
 | 
						|
                SpeakArrayString(AI_TALK_ON_DAMAGED);
 | 
						|
            }
 | 
						|
            // 1.4 note: These two variables are currently *unused* apart from
 | 
						|
            // healing. When healing a being (even another NPC) they are checked
 | 
						|
            // for massive damage. Can not bother to set the highest damager for now.
 | 
						|
            // NEW:
 | 
						|
            int nHighestDamage = GetAIInteger(AI_HIGHEST_DAMAGE_AMOUNT);
 | 
						|
            if(nDamage >= nHighestDamage)
 | 
						|
            {
 | 
						|
                SetAIInteger(AI_HIGHEST_DAMAGE_AMOUNT, nDamage);
 | 
						|
            }
 | 
						|
 | 
						|
            /* OLD:
 | 
						|
 | 
						|
            // Get the previous highest damager, and highest damage amount
 | 
						|
            object oHighestDamager = GetAIObject(AI_HIGHEST_DAMAGER);
 | 
						|
            int nHighestDamage = GetAIInteger(AI_HIGHEST_DAMAGE_AMOUNT);
 | 
						|
            // Set the highest damager, if they are seen or heard, and have done loads.
 | 
						|
            if((GetObjectSeen(oDamager) || GetObjectHeard(oDamager)) &&
 | 
						|
                nDamage >= nHighestDamage || !GetIsObjectValid(oHighestDamager))
 | 
						|
            {
 | 
						|
                SetAIObject(AI_HIGHEST_DAMAGER, oDamager);
 | 
						|
                SetAIInteger(AI_HIGHEST_DAMAGE_AMOUNT, nDamage);
 | 
						|
            }
 | 
						|
            // Else, if the original was not valid...or not seen/heard, we
 | 
						|
            // delete it so we don't bother to use it later.
 | 
						|
            else if(!GetIsObjectValid(oHighestDamager) ||
 | 
						|
              (!GetObjectSeen(oHighestDamager) && !GetObjectHeard(oHighestDamager)))
 | 
						|
            {
 | 
						|
                DeleteAIObject(AI_HIGHEST_DAMAGER);
 | 
						|
                DeleteAIInteger(AI_HIGHEST_DAMAGE_AMOUNT);
 | 
						|
            }
 | 
						|
            */
 | 
						|
 | 
						|
            // Get all the physical damage. Elemental damage is then nDamage minus
 | 
						|
            // the physical damage.
 | 
						|
            int nPhysical = GetDamageDealtByType(DAMAGE_TYPE_BASE_WEAPON |
 | 
						|
                                                 DAMAGE_TYPE_BLUDGEONING |
 | 
						|
                                                 DAMAGE_TYPE_PIERCING |
 | 
						|
                                                 DAMAGE_TYPE_SLASHING);
 | 
						|
            // If they are all -1, then we make nPhysical 0.
 | 
						|
            if(nPhysical <= -1) nPhysical = 0;
 | 
						|
 | 
						|
            // Physical damage - only sets if the last damager is the last attacker.
 | 
						|
            if(GetAIObject(AI_STORED_LAST_ATTACKER) == oDamager)
 | 
						|
            {
 | 
						|
                // Get the previous highest damage and test it
 | 
						|
                if(nPhysical > GetAIInteger(AI_HIGHEST_PHISICAL_DAMAGE_AMOUNT))
 | 
						|
                {
 | 
						|
                    // If higher, and was a melee/ranged attacker, set it.
 | 
						|
                    // This does include other additional physical damage - EG:
 | 
						|
                    // weapon property: Bonus Damage.
 | 
						|
                    SetAIInteger(AI_HIGHEST_PHISICAL_DAMAGE_AMOUNT, nPhysical);
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            // Set the max elemental damage done, for better use of elemental
 | 
						|
            // protections. This is set for the most damage...so it could be
 | 
						|
            // 1 (for a +1 fire weapon, any number of hits) or over 50 (good
 | 
						|
            // fireball/flame storm etc.)
 | 
						|
            int nElemental = nDamage - nPhysical;
 | 
						|
            if(nElemental > GetAIInteger(MAX_ELEMENTAL_DAMAGE))
 | 
						|
            {
 | 
						|
                SetAIInteger(MAX_ELEMENTAL_DAMAGE, nElemental);
 | 
						|
            }
 | 
						|
            // Set the last damage done, may set to 0 of course :-P
 | 
						|
            // * This is only set if they did damage us at all, however.
 | 
						|
            SetAIInteger(LAST_ELEMENTAL_DAMAGE, nElemental);
 | 
						|
 | 
						|
            // Morale: We may get a penalty if it does more than a cirtain amount of HP damage.
 | 
						|
            // Other: We set highest damager and amount.
 | 
						|
            if(!GetSpawnInCondition(AI_FLAG_FLEEING_FEARLESS, AI_TARGETING_FLEE_MASTER))
 | 
						|
            {
 | 
						|
                // Get penalty and how much damage at once needs to be done
 | 
						|
                int nPenalty = GetBoundriedAIInteger(AI_DAMAGE_AT_ONCE_PENALTY, 6, 50, 1);
 | 
						|
                int nToDamage = GetBoundriedAIInteger(AI_DAMAGE_AT_ONCE_FOR_MORALE_PENALTY, GetMaxHitPoints()/6, GetMaxHitPoints(), 1);
 | 
						|
                if(nDamage > nToDamage)
 | 
						|
                {
 | 
						|
                    // 61: "[Damaged] Morale Penalty for 600 seconds [Penalty]" + IntToString(iPenalty)
 | 
						|
                    DebugActionSpeakByInt(61, OBJECT_INVALID, nPenalty);
 | 
						|
                    // Apply penalty
 | 
						|
                    SetMoralePenalty(nPenalty, 300.0);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // If we are not attacking anything, and not in combat, react!
 | 
						|
        if(!CannotPerformCombatRound())
 | 
						|
        {
 | 
						|
            // 62: "[Damaged] Not in combat: DCR [Damager]" + GetName(oDamager)
 | 
						|
            DebugActionSpeakByInt(62, oDamager);
 | 
						|
 | 
						|
            // Check if they are in the same area. Can be a left AOE spell.
 | 
						|
            // Don't attack purposly across area's.
 | 
						|
            if(GetArea(oDamager) == GetArea(OBJECT_SELF))
 | 
						|
            {
 | 
						|
                // Shout to allies to attack the enemy who attacked me
 | 
						|
                AISpeakString(AI_SHOUT_I_WAS_ATTACKED);
 | 
						|
 | 
						|
                DetermineCombatRound(oDamager);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                // Shout to allies to attack, or be prepared.
 | 
						|
                AISpeakString(AI_SHOUT_CALL_TO_ARMS);
 | 
						|
 | 
						|
                DetermineCombatRound();
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            // Shout to allies to attack, or be prepared.
 | 
						|
            AISpeakString(AI_SHOUT_CALL_TO_ARMS);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    // Else it is friendly, or invalid damager
 | 
						|
    else
 | 
						|
    {
 | 
						|
        // Still will react - eg: A left AOE spell (which might mean a battle
 | 
						|
        // just happened)
 | 
						|
        if(!CannotPerformCombatRound())
 | 
						|
        {
 | 
						|
            // Shout to allies to attack generally. No target to specifically attack,
 | 
						|
            // as it is an ally.
 | 
						|
            AISpeakString(AI_SHOUT_CALL_TO_ARMS);
 | 
						|
 | 
						|
            // 63: [Damaged] Not in combat: DCR. Ally hit us. [Damager(Ally?)]" + GetName(oDamager)
 | 
						|
            DebugActionSpeakByInt(63, oDamager);
 | 
						|
            DetermineCombatRound();
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            // Shout to allies to attack, or be prepared.
 | 
						|
            AISpeakString(AI_SHOUT_CALL_TO_ARMS);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    // User defined event - for normally immoral creatures.
 | 
						|
    if(GetCurrentHitPoints() == 1)
 | 
						|
    {
 | 
						|
        // Fire the immortal damaged at 1 HP event.
 | 
						|
        FireUserEvent(AI_FLAG_UDE_DAMAGED_AT_1_HP, EVENT_DAMAGED_AT_1_HP);
 | 
						|
    }
 | 
						|
    // Fire End of Damaged event
 | 
						|
    FireUserEvent(AI_FLAG_UDE_DAMAGED_EVENT, EVENT_DAMAGED_EVENT);
 | 
						|
}
 |