Updated Jasperre's AI

Updated Jasperre's AI to 1.4, fixed a few other coding bugs & fully compiled module.
This commit is contained in:
Jaysyn904
2021-08-30 17:31:44 -04:00
parent 2e0b5b615b
commit 1c1c90e986
404 changed files with 11239 additions and 7881 deletions

View File

@@ -1,3 +1,5 @@
#include "drownud"
void main()
{
object oCurrentObject = GetFirstObjectInArea();

View File

@@ -23,7 +23,7 @@ effects come in.
void main()
{
//Edit Settings here ///////////////////////////////////////////////////////
float z = 14; //How High the fireworks go before exploding
float z = 14.0; //How High the fireworks go before exploding
//DO NOT EDIT BELOW THIS LINE //////////////////////////////////////////////
object oArea = GetArea(OBJECT_SELF);
object oTarget = GetObjectByTag("FireworksSource");

View File

@@ -1,6 +1,6 @@
/************************ [Destroy Ourself] ************************************
/*/////////////////////// [Destroy Ourself] ////////////////////////////////////
Filename: J_AI_DestroySelf
************************* [Destroy Ourself] ************************************
///////////////////////// [Destroy Ourself] ////////////////////////////////////
This is executed OnDeath to clean up the corpse. It helps - clears all
non-droppable stuff.
@@ -10,22 +10,23 @@
Oh, if this is executed any other time when they are dead, they are
destroyed instantly.
************************* [History] ********************************************
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Added to replace a include function for death.
- No locals are destroyed. The game should do that anyway. Items are, though.
************************* [Workings] *******************************************
1.4 -
///////////////////////// [Workings] ///////////////////////////////////////////
this, if ever fired, will destroy the creature. It is not deleayed - there
is a special function in the death script to check the whole "Did I get
raised?" stuff.
I suppose you can edit this to put a corpse in :-D
************************* [Arguments] ******************************************
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: N/A - none needed.
************************* [Destroy Ourself] ***********************************/
///////////////////////// [Destroy Ourself] //////////////////////////////////*/
// Exectued from death, to speed things up.
#include "j_inc_constants"
#include "J_INC_CONSTANTS"
// This will delete all un-droppable items, before they fade out.
void DeleteAllThings();

View File

@@ -1,6 +1,6 @@
/************************ [Execute Combat Action] ******************************
/*/////////////////////// [Execute Combat Action] //////////////////////////////
Filename: J_AI_DeterCombat
************************* [Execute Combat Action] ******************************
///////////////////////// [Execute Combat Action] //////////////////////////////
Fired from other scripts, this runs an actual actions.
It also contains the Pre-combat and Post-combat action events. In essense
@@ -10,10 +10,12 @@
Therefore, they only fire if the default AI is being used :-)
Do NOT mess with this file, please. :-D
************************* [History] ********************************************
///////////////////////// [History] ////////////////////////////////////////////
1.3 - AI Include only fired from here. This script is executed from others,
as the default in place of custom AI scripts.
************************* [Workings] *******************************************
1.4 - Mainly See the generic AI include file for changes. This is just
what calls DetermineCombatRound() and associated things.
///////////////////////// [Workings] ///////////////////////////////////////////
This is simple:
- We execute it if there is no other AI files.
@@ -25,11 +27,11 @@
It cleans things up, and is the only script in the whole set that has
j_inc_generic_ai in, reducing file size, and compile times. AI is more
manageable too!
************************* [Arguments] ******************************************
Arguments: See J_Inc_Generic_AI
************************* [Execute Combat Action] *****************************/
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: See J_INC_GENERIC_AI
///////////////////////// [Execute Combat Action] ////////////////////////////*/
#include "j_inc_generic_ai"
#include "J_INC_GENERIC_AI"
void main()
{
@@ -41,8 +43,12 @@ void main()
// Check: Are we imputting a target? We imputt it even if invalid
object oTarget = GetLocalObject(OBJECT_SELF, AI_TEMP_SET_TARGET);
// Speak combat round speakstring
SpeakArrayString(AI_TALK_ON_COMBAT_ROUND, TRUE);
// * Don't speak when dead. 1.4 change (an obvious one to make)
if(CanSpeak())
{
// Speak combat round speakstring
SpeakArrayString(AI_TALK_ON_COMBAT_ROUND, TRUE);
}
// Call combat round using include
AI_DetermineCombatRound(oTarget);

View File

@@ -1,27 +1,26 @@
/************************ [On Heartbeat - Animations] **************************
/*/////////////////////// [On Heartbeat - Animations] //////////////////////////
Filename: J_AI_Heart_aimat
************************* [On Heartbeat - Animations] **************************
///////////////////////// [On Heartbeat - Animations] //////////////////////////
To keep the heartbeat small, I've divided all the bits that MIGHT fire
into other scripts. This makes it smaller, and faster.
I've also shortened the perception script too - and along with the heartbeat
is the largest, Out-Of-Combat script. It isn't divided up by execute scripts,
but should be leaner.
************************* [History] ********************************************
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Added to speed up heartbeat and keep filesize down on un-used parts.
************************* [Workings] *******************************************
1.4 - Changed to nw_i0_generic, as to include all animations, whatever NwN version.
///////////////////////// [Workings] ///////////////////////////////////////////
This is executed as file HEARTBEAT_ANIMATIONS_FILE, from the heartbeat
script (Default1, or onheartbeat). It can run by itself, using the SoU
animations - better then me changing and making my own, as they are vastly
improved from NwN!
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: N/A
///////////////////////// [On Heartbeat - Animations] ////////////////////////*/
It is Not called if AI_VALID_ANIMATIONS AI Integer is not set.
************************* [Arguments] ******************************************
Arguments:
************************* [On Heartbeat - Animations] *************************/
// SoU Animations file.
#include "x0_i0_anims"
// Generic Include File. This contains animations whatever NwN version it is.
#include "NW_I0_GENERIC"
void main()
{
@@ -37,8 +36,4 @@ void main()
{
PlayImmobileAmbientAnimations();
}
else
{
DeleteLocalInt(OBJECT_SELF, "AI_INTEGER" + "AI_VALID_ANIMATIONS");
}
}

View File

@@ -1,46 +1,57 @@
/************************ [On Heartbeat - Buff] ********************************
/*/////////////////////// [On Heartbeat - Buff] ////////////////////////////////
Filename: j_ai_heart_buff
************************* [On Heartbeat - Buff] ********************************
///////////////////////// [On Heartbeat - Buff] ////////////////////////////////
This is ExecuteScript'ed from the heartbeat file, if they want to buff
themselves with spells to be prepared for any battle coming up.
************************* [History] ********************************************
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Added
************************* [Workings] *******************************************
1.4 - Will Add all the appropriate Hordes spells, notably the missing epic ones.
///////////////////////// [Workings] ///////////////////////////////////////////
This contains Advance Buffing - IE quick protection spells.
I've done what ones I think are useful - IE most protection ones & summons!
This doesn't include any which are very short duration:
This doesn't include any which are (potentionally) very short duration (IE:
1 round/level, or a set value):
Elemntal shield/Wounding whispers (though you can add all of these!)
Aid, bless, aura of vitality, aura of glory, blood frenzy, prayer,
divine* Range (Power, Might, Shield, Favor), Expeditious retreat,
holy/unholy aura (or protection from /magic circle against),
natures balance, one with the land, shield of faith, virtue, war cry.
************************* [Arguments] ******************************************
natures balance, one with the land, shield of faith, virtue, war cry, dirge,
death armor, mestals acid sheath.
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: N/A
************************* [On Heartbeat - Buff] *******************************/
///////////////////////// [On Heartbeat - Buff] //////////////////////////////*/
// Constants for some unconstantanated spells
#include "J_INC_CONSTANTS"
// Wrapper, to stop repeating the same lines! :-)
int BuffCastSpell(int iSpell);
int BuffCastSpell(int nSpell);
void main()
{
// For summons counter.
int nCnt, iBreak;
int nCnt, bBreak;
// FAST BUFF SELF
// Stop what we are doing first, to perform the actions.
ClearAllActions();
//Combat Protections
// Always cast "Epic Warding" and "Epic Mage Armor".
BuffCastSpell(FEAT_EPIC_SPELL_EPIC_WARDING);
BuffCastSpell(FEAT_EPIC_SPELL_MAGE_ARMOUR);
// Combat Protections
if(!BuffCastSpell(SPELL_PREMONITION))
if(!BuffCastSpell(SPELL_GREATER_STONESKIN))
BuffCastSpell(SPELL_STONESKIN);
//Visage Protections
// Visage Protections
if(!BuffCastSpell(SPELL_SHADOW_SHIELD))
if(!BuffCastSpell(SPELL_ETHEREAL_VISAGE))
BuffCastSpell(SPELL_GHOSTLY_VISAGE);
//Mantle Protections
// Mantle Protections
if(!BuffCastSpell(SPELL_GREATER_SPELL_MANTLE))
if(!BuffCastSpell(SPELL_SPELL_MANTLE))
BuffCastSpell(SPELL_LESSER_SPELL_MANTLE);
@@ -49,13 +60,13 @@ void main()
if(BuffCastSpell(SPELL_TRUE_SEEING))
BuffCastSpell(SPELL_SEE_INVISIBILITY);
//Elemental Protections. 4 lots. From 40/- to 10/-
// Elemental Protections. 4 lots. From 40/- to 10/-
if(!BuffCastSpell(SPELL_ENERGY_BUFFER))
if(!BuffCastSpell(SPELL_PROTECTION_FROM_ELEMENTS))
if(!BuffCastSpell(SPELL_RESIST_ELEMENTS))
BuffCastSpell(SPELL_ENDURE_ELEMENTS);
//Mental Protections
// Mental Protections
if(!BuffCastSpell(SPELL_MIND_BLANK))
if(!BuffCastSpell(SPELL_LESSER_MIND_BLANK))
BuffCastSpell(SPELL_CLARITY);
@@ -64,7 +75,7 @@ void main()
if(!BuffCastSpell(SPELL_GLOBE_OF_INVULNERABILITY))
BuffCastSpell(SPELL_MINOR_GLOBE_OF_INVULNERABILITY);
//Invisibility
// Invisibility
// Note: Improved has 50% consealment, etherealness has just invisiblity.
if(!BuffCastSpell(SPELL_IMPROVED_INVISIBILITY))
BuffCastSpell(SPELL_DISPLACEMENT);//50% consealment
@@ -82,21 +93,22 @@ void main()
BuffCastSpell(SPELL_EAGLE_SPLEDOR);
if(!BuffCastSpell(SPELL_GREATER_FOXS_CUNNING))
BuffCastSpell(SPELL_FOXS_CUNNING);
if(!BuffCastSpell(SPELL_GREATER_OWLS_WISDOM))
BuffCastSpell(SPELL_OWLS_WISDOM);
if(!BuffCastSpell(SPELL_GREATER_ENDURANCE))
BuffCastSpell(SPELL_ENDURANCE);
if(!BuffCastSpell(AI_SPELL_OWLS_INSIGHT))
if(!BuffCastSpell(SPELL_GREATER_OWLS_WISDOM))
BuffCastSpell(SPELL_OWLS_WISDOM);
// Mage armor or shield. Don't stack them.
if(!BuffCastSpell(SPELL_SHIELD))
BuffCastSpell(SPELL_MAGE_ARMOR);
// Entropic Shield (20% consealment, 1 turn/level)
BuffCastSpell(SPELL_ENTROPIC_SHIELD);
// Protection from negative energy
if(!BuffCastSpell(SPELL_UNDEATHS_ETERNAL_FOE))
BuffCastSpell(SPELL_DEATH_WARD);
//Misc Protections which have no more powerful.
// Low durations (Rounds per caster level)
// if(!BuffCastSpell(SPELL_ELEMENTAL_SHIELD))
// BuffCastSpell(SPELL_WOUNDING_WHISPERS);
BuffCastSpell(SPELL_BARKSKIN);
BuffCastSpell(SPELL_ENTROPIC_SHIELD);
BuffCastSpell(SPELL_PROTECTION_FROM_SPELLS);
@@ -105,6 +117,12 @@ void main()
BuffCastSpell(SPELL_REGENERATE);
BuffCastSpell(SPELL_SPELL_RESISTANCE);
BuffCastSpell(SPELL_FREEDOM_OF_MOVEMENT);
BuffCastSpell(SPELL_FREEDOM_OF_MOVEMENT);
// Low durations (Rounds per caster level)
// if(!BuffCastSpell(SPELL_ELEMENTAL_SHIELD))
// BuffCastSpell(SPELL_WOUNDING_WHISPERS);
// BuffCastSpell(SPELL_DEATH_ARMOR);
//Summon Ally.
@@ -125,13 +143,13 @@ void main()
{
// 8, 7, 6, 5.
for(nCnt = SPELL_SUMMON_CREATURE_VIII;
(nCnt >= SPELL_SUMMON_CREATURE_V && iBreak != TRUE);
(nCnt >= SPELL_SUMMON_CREATURE_V && bBreak != TRUE);
nCnt--)
{
if(BuffCastSpell(nCnt)) iBreak = TRUE;
if(BuffCastSpell(nCnt)) bBreak = TRUE;
}
// Then undead
if(iBreak != TRUE)
if(bBreak != TRUE)
{
if(!BuffCastSpell(SPELL_CREATE_GREATER_UNDEAD))
{
@@ -141,7 +159,7 @@ void main()
{
// Lastly, the 4-1 ones.
for(nCnt = SPELL_SUMMON_CREATURE_IV;
(nCnt >= SPELL_SUMMON_CREATURE_I);
nCnt >= SPELL_SUMMON_CREATURE_I;
nCnt--)
{
if(BuffCastSpell(nCnt)) break;
@@ -159,11 +177,11 @@ void main()
}
// Wrapper, to stop repeating the same lines! :-)
int BuffCastSpell(int iSpell)
int BuffCastSpell(int nSpell)
{
if(GetHasSpell(iSpell))
if(GetHasSpell(nSpell))
{
ActionCastSpellAtObject(iSpell, OBJECT_SELF, METAMAGIC_ANY, FALSE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
ActionCastSpellAtObject(nSpell, OBJECT_SELF, METAMAGIC_ANY, FALSE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
return TRUE;
}
return FALSE;

View File

@@ -1,35 +1,36 @@
/************************ [On Heartbeat - Move Nearer PC] **********************
Filename: j_ai_heart_serch
************************* [On Heartbeat - Move Nearer PC] **********************
/*/////////////////////// [On Heartbeat - Move Nearer PC] //////////////////////
Filename: J_AI_Heart_Serch
///////////////////////// [On Heartbeat - Move Nearer PC] //////////////////////
This is fired to perform moving nearer the PC, or searching towards them.
Yes, like the Henchmen and Battle AI code. I am sure they won't mind - if
they do, I will remove it :-)
************************* [History] ********************************************
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Added
************************* [Workings] *******************************************
1.4 - Reformatted as with the rest of the AI
///////////////////////// [Workings] ///////////////////////////////////////////
This makes the NPC move nearer to a PC. Fires if the spawn condition
is on and the timer is not, and only 1/4 heartbeats to lessen the effect.
************************* [Arguments] ******************************************
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: N/A
************************* [On Heartbeat - Move Nearer PC] *********************/
///////////////////////// [On Heartbeat - Move Nearer PC] ////////////////////*/
#include "j_inc_constants"
#include "J_INC_CONSTANTS"
void main()
{
object oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC);
if(GetIsObjectValid(oPC) && GetIsEnemy(oPC) &&
GetDistanceToObject(oPC) <
IntToFloat(GetBoundriedAIInteger(AI_SEARCH_IF_ENEMIES_NEAR_RANGE, i25, i50, i5)))
IntToFloat(GetBoundriedAIInteger(AI_SEARCH_IF_ENEMIES_NEAR_RANGE, 25, 50, 5)))
{
vector vPC = GetPosition(oPC);
// Whats the distance we use to move a bit nearer?
int iRandom = i10 + Random(i10);
int nRandom = 10 + Random(10);
// Randomise a point nearby.
vPC.x += IntToFloat(iRandom - (Random(i2 * iRandom + i1)));
vPC.y += IntToFloat(iRandom - (Random(i2 * iRandom + i1)));
vPC.z += IntToFloat(iRandom - (Random(i2 * iRandom + i1)));
vPC.x += IntToFloat(nRandom - (Random(2 * nRandom + 1)));
vPC.y += IntToFloat(nRandom - (Random(2 * nRandom + 1)));
vPC.z += IntToFloat(nRandom - (Random(2 * nRandom + 1)));
// Define the location
location lNew = Location(GetArea(oPC), vPC, IntToFloat(Random(359)));
SetLocalTimer(AI_TIMER_SEARCHING, GetDistanceToObject(oPC));

View File

@@ -0,0 +1,332 @@
/*/////////////////////// [On Blocked] /////////////////////////////////////////
Filename: J_AI_OnBlocked or nw_c2_defaulte
///////////////////////// [On Blocked] /////////////////////////////////////////
Added in user defined constant - won't open any doors.
0 = Default (even if not set) opens as appropriate
1 = Always bashes the door.
2 = Never open any doors
3 = Never opens plot doors
They will: (int is intellgience needed)
1. Open if not trapped (7 int)
2. Unlock if possible, and rank is high enough, and it needs no key and is not trapped (7 int)
3. Untrap the door, if possible, if trapped (7 int)
4. Else, if has high enough stats, try Knock. (10 int)
6. Else Equip appropriate weapons and bash (5 int)
Note: This also fires for blocking via. creatures. It is optimised, and
works by re-targeting and doing a few small things to do with blocking.
///////////////////////// [History] ////////////////////////////////////////////
1.0 - Opens with Knock. Unlocks door. Ignores trapped doors.
1.3 - Debug messages.
- New events, even if the change of using them is small!
- No ClearAllactions so any previous movings will carry on once the door is gone.
- Removed debug messages
- Added Creature reaction code
1.4 - Need to add a "hands" check (done on spawn, to set a setting to not
open doors at all, IE: We do NOT have hands, do not open doors), so
its a little more realistic "out of the box"
- Fixed an instance of GetObjectSeen being repeated.
- Fixed the variable AI_DOOR_INTELLIGENCE not being got via GetAIInteger().
- Removed unneeded else statement.
///////////////////////// [Workings] ///////////////////////////////////////////
Uses simple code to deal with a door in the best way possible.
Uses DoDoorAction, which is added to the top of an action queue and doesn't,
therefore, delete any ActionAttack's and so on below it. (Or I hope it
is like this)
Creatures are reacted by with ClearAllActions usually.
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: GetBlockingDoor, GetIsDoorActionPossible, GetLocked, GetLockKeyRequired
GetLockKeyTag, GetLockUnlockDC, GetPlotFlag, DoDoorAction
///////////////////////// [On Blocked] ///////////////////////////////////////*/
#include "J_INC_OTHER_AI"
// Fires the end-blocked event.
void FireBlockedEvent();
// Range attack oTarget.
int RangedAttack(object oTarget = OBJECT_INVALID);
void main()
{
// Pre-on blocked-event. Returns TRUE if we interrupt this script call.
if(FirePreUserEvent(AI_FLAG_UDE_ON_BLOCKED_PRE_EVENT, EVENT_ON_BLOCKED_PRE_EVENT)) return;
// AI status check. Is the AI on?
if(GetAIOff()) return;
// This CAN return a blocking creature.
object oBlocker = GetBlockingDoor();
int nBlockerType = GetObjectType(oBlocker);
if(!GetIsObjectValid(oBlocker)) return;
// Anyone blocked by an enemy will re-target them (and attack them), blocked
// by someone they cannot get they will cast seeing spells and react, and if
// blocked by a friend, they may run back and use a ranged weapon if they
// have one.
if(nBlockerType == OBJECT_TYPE_CREATURE)
{
// Are we doing something that should not be overriden? (even fleeing,
// if stuck, we can't do anything else then move again on heartbeat!)
if(GetIsPerformingSpecialAction()) return;
// Blocked timer, we normally do an action. A small timer stops a lot
// of lag.
if(GetLocalTimer(AI_TIMER_BLOCKED)) return;
// Set the timer for 1 second
SetLocalTimer(AI_TIMER_BLOCKED, 1.0);
// Is it an enemy?
if(GetIsEnemy(oBlocker))
{
// Check if seen or heard
if(GetObjectSeen(oBlocker) || GetObjectHeard(oBlocker))
{
// Enemy :-) We can re-target (as know of thier presence), using
// them as a target.
// - This overrides even casting a spell - basically, as we should
// be moving, this will re-cast it at someone or something in range
SetAIObject(AI_ATTACK_SPECIFIC_OBJECT, oBlocker);
// Check if we can do combat - if we cannot, we can re-do combat
// next time
if(!GetIsBusyWithAction())
{
// Attacks if we are not attacking
ClearAllActions();
DetermineCombatRound(oBlocker);
return;
}
}
else
{
// Invisible? Not there? Some odd error? We set that we know of
// someone invisible, and will attack if not in combat.
if(GetHasEffect(EFFECT_TYPE_INVISIBILITY, oBlocker) ||
GetStealthMode(oBlocker) == STEALTH_MODE_ACTIVATED)
{
SetAIObject(AI_LAST_TO_GO_INVISIBLE, oBlocker);
}
// Shout to allies
AISpeakString(AI_SHOUT_CALL_TO_ARMS);
// Check if we can do combat
if(!GetIsBusyWithAction())
{
// Attacks if we are not attacking
ClearAllActions();
DetermineCombatRound();
return;
}
}
}
// Else is non-enemy, a friend or neutral
else
{
// As we are blocked by them, we re-do combat - we have a choice of
// either using a Bow to attack our target (if that was what
// we were doing) and move back a little, or re-initiate combat
// Were we attacking in combat?
object oPrevious = GetAttackTarget();
// Check action
if(GetCurrentAction() == ACTION_ATTACKOBJECT)
{
// This gets set to FALSE if we can cutthrough attack,
// or whatever.
int bPreviousAttackFailed = FALSE;
// Check if we can see our previous target
if(GetObjectSeen(oPrevious) ||
(GetObjectHeard(oPrevious) && LineOfSightObject(OBJECT_SELF, oPrevious)))
{
// We can! see if we can re-attack with ranged weapon, else
// doesn't matter we can see them
bPreviousAttackFailed = RangedAttack(oPrevious);
}
// If we havn't added an action yet...
if(bPreviousAttackFailed == FALSE)
{
// We have not stopped the script - so determine combat
// round against nearest seen or heard enemy!
if(!RangedAttack())
{
// Else normal round to try and get a new target
ClearAllActions();
DetermineCombatRound();
}
}
// Action attack, normally means melee attack. If we can, we
// attack our previous target if seen, ELSE we will re-initate
// combat.
AISpeakString(AI_SHOUT_I_WAS_ATTACKED);
// Fire the On blocked event as normal
FireBlockedEvent();
return;
}
else // if(nAction == ACTION_CASTSPELL and others)
{
// Reinitate combat, but don't attack oPrevious
ClearAllActions();
DetermineCombatRound();
// Action attack, normally means melee attack. If we can, we
// attack our previous target if seen, ELSE we will re-initate
// combat.
AISpeakString(AI_SHOUT_I_WAS_ATTACKED);
// Fire the On blocked event as normal
FireBlockedEvent();
return;
}
}
}
// Placeable - Currently not returned, however, added just in case!
else if(nBlockerType == OBJECT_TYPE_PLACEABLE)
{
// Check for plot, and therefore attack it to bring it down.
// - Remember, ActionAttack will re-initiate when combat round fires
// again in 3 or 6 seconds (or less, if we just were moving)
if(!GetPlotFlag(oBlocker) &&
GetIsPlaceableObjectActionPossible(oBlocker, PLACEABLE_ACTION_BASH))
{
// Do placeable action
DoPlaceableObjectAction(oBlocker, PLACEABLE_ACTION_BASH);
FireBlockedEvent();
return;
}
return;
}
// Door behaviour
else if(nBlockerType == OBJECT_TYPE_DOOR)
{
int nDoorIntelligence = GetAIInteger(AI_DOOR_INTELLIGENCE);
int nInt = GetAbilityScore(OBJECT_SELF, ABILITY_INTELLIGENCE);
if(nDoorIntelligence == 1)// 1 = Always bashes the doors, plot, locked or anything.
{
DoDoorAction(oBlocker, DOOR_ACTION_BASH);
// We re-initiate combat.
FireBlockedEvent();
return;
}
else if(nDoorIntelligence == 2)// 2 = Never open anything, bashing or not.
{
FireBlockedEvent();
return;
}
else if(nDoorIntelligence == 3)// 3 = Never tries anything against plot doors.
{
if(GetPlotFlag(oBlocker))
{
FireBlockedEvent();
return;
}
}
if(nInt >= 5)
{
// Need some intelligence :-)
if(nInt >= 7)
{
// Right, first, we may...shock...open it!!!
// Checks Key, lock, trap and if the action is possible.
if(GetIsDoorActionPossible(oBlocker, DOOR_ACTION_OPEN) &&
!GetLocked(oBlocker) &&
!GetIsTrapped(oBlocker) &&
(!GetLockKeyRequired(oBlocker) ||
(GetLockKeyRequired(oBlocker) && GetItemPossessor(GetObjectByTag(GetLockKeyTag(oBlocker))) == OBJECT_SELF)))
{
DoDoorAction(oBlocker, DOOR_ACTION_OPEN);
FireBlockedEvent();
return;
}
// Unlock it with the skill, if it is not trapped and we can :-P
// We take 20 off the door DC, thats our minimum roll, after all.
if(GetLocked(oBlocker) &&
!GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_OPENING_LOCKED_DOORS, AI_OTHER_COMBAT_MASTER) &&
!GetLockKeyRequired(oBlocker) && GetHasSkill(SKILL_OPEN_LOCK) &&
GetIsDoorActionPossible(oBlocker, DOOR_ACTION_UNLOCK) && !GetIsTrapped(oBlocker) &&
(GetSkillRank(SKILL_OPEN_LOCK) >= (GetLockLockDC(oBlocker) - 20)))
{
DoDoorAction(oBlocker, DOOR_ACTION_UNLOCK);
FireBlockedEvent();
return;
}
// Specilist thing - knock
if(nInt >= 10)
{
if((GetIsDoorActionPossible(oBlocker, DOOR_ACTION_KNOCK)) &&
GetLockUnlockDC(oBlocker) <= 25 &&
!GetLockKeyRequired(oBlocker) && GetHasSpell(SPELL_KNOCK))
{
DoDoorAction(oBlocker, DOOR_ACTION_KNOCK);
FireBlockedEvent();
return;
}
}
}
// If Our Int is over 5, we will bash after everything else.
if(GetIsDoorActionPossible(oBlocker, DOOR_ACTION_BASH) && !GetPlotFlag(oBlocker))
{
if(GetAttackTarget() != oBlocker)
{
DoDoorAction(oBlocker, DOOR_ACTION_BASH);
}
FireBlockedEvent();
return;
}
}
}
// Fire Blocked event
FireBlockedEvent();
}
// Fires the end-blocked event.
void FireBlockedEvent()
{
// Fire End-blocked-UDE
FireUserEvent(AI_FLAG_UDE_ON_BLOCKED_EVENT, EVENT_ON_BLOCKED_EVENT);
}
// Range attack oTarget.
int RangedAttack(object oTarget)
{
// If we are primarily melee, don't use this
if(!GetSpawnInCondition(AI_FLAG_COMBAT_BETTER_AT_HAND_TO_HAND, AI_COMBAT_MASTER)) return FALSE;
object oRangedTarget = oTarget;
if(!GetIsObjectValid(oRangedTarget))
{
oRangedTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE);
if(!GetIsObjectValid(oTarget))
{
oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD, CREATURE_TYPE_IS_ALIVE, TRUE);
// heard must be in LOS to attack, as we are probably stuck
if(!GetIsObjectValid(oTarget) && LineOfSightObject(OBJECT_SELF, oRangedTarget))
{
return FALSE;
}
}
}
// Ranged weapon attack against oTarget
// doesn't matter we can see them
object oRanged = GetAIObject(AI_WEAPON_RANGED);
int nAmmo = GetAIInteger(AI_WEAPON_RANGED_AMMOSLOT);
// Check ammo and validness
if(GetIsObjectValid(oRanged) && (nAmmo == INVENTORY_SLOT_RIGHTHAND ||
GetIsObjectValid(GetItemInSlot(nAmmo))))
{
// Attack with it
ClearAllActions();
ActionEquipItem(oRanged, INVENTORY_SLOT_RIGHTHAND);
ActionAttack(oRangedTarget);
// Stop
return TRUE;
}
return FALSE;
}

View File

@@ -0,0 +1,37 @@
/*/////////////////////// [On Combat Round End] ////////////////////////////////
Filename: nw_c2_default3 or J_AI_OnCombatrou
///////////////////////// [On Combat Round End] ////////////////////////////////
This is run every 3 or 6 seconds, if the creature is in combat. It is
executed only in combat automatically.
It runs what the AI should do, bascially.
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Executes same script as the other parts of the AI to cuase a new action
1.4 -
///////////////////////// [Workings] ///////////////////////////////////////////
Calls the combat AI file using the J_INC_OTHER_AI include function,
DetermineCombatRound.
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: GetAttackTarget, GetLastHostileActor, GetAttemptedAttackTarget,
GetAttemptedSpellTarget (Or these are useful at least!)
///////////////////////// [On Combat Round End] //////////////////////////////*/
#include "J_INC_OTHER_AI"
void main()
{
// Pre-combat-round-event. Returns TRUE if we interrupt this script call.
if(FirePreUserEvent(AI_FLAG_UDE_END_COMBAT_ROUND_PRE_EVENT, EVENT_END_COMBAT_ROUND_PRE_EVENT)) return;
// AI status check. Is the AI on?
if(GetAIOff()) return;
// It is our normal call (every 3 or 6 seconds, when we can change actions)
// so no need to delete, and we fire the UDE's.
// Determine combat round against an invalid target (as default)
DetermineCombatRound();
// Fire End of end combat round event
FireUserEvent(AI_FLAG_UDE_END_COMBAT_ROUND_EVENT, EVENT_END_COMBAT_ROUND_EVENT);
}

View File

@@ -0,0 +1,160 @@
/*/////////////////////// [On Conversation] ////////////////////////////////////
Filename: J_AI_OnConversat or nw_c2_default4
///////////////////////// [On Conversation] ////////////////////////////////////
OnConversation/ Listen to shouts.
Documented, and checked. -Working-
Added spawn in condition - Never clear actions when talking.
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Added in conversation thing - IE we can set speakstrings, no need for conversation file.
- Sorted more shouts out.
- Should work right, and not cause too many actions (as we ignore
shouts for normally 12 or so seconds before letting them affect us again).
1.4 - Deafness incorpreated.
///////////////////////// [Workings] ///////////////////////////////////////////
Uses RespondToShout to react to allies' shouts, and just attacks any enemy
who speaks, or at least moves to them. (OK, dumb if they are invisible, but
oh well, they shouldn't talk so loud!)
Remember, whispers are never heard if too far away, speakstrings don't go
through walls, and shouts are always heard (so we don't go off to anyone
not in our area, remember)
Deafness causes us to never hear battle, so unless we see the target speaking
we do not react. Doesn't apply to normal conversations - although if we cannot
talk (also restricted by deafness) then so be it.
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: GetListenPatternNumber, GetLastSpeaker, TestStringAgainstPattern,
GetMatchedSubstring
///////////////////////// [On Conversation] //////////////////////////////////*/
#include "J_INC_OTHER_AI"
void main()
{
// Pre-conversation-event. Returns TRUE if we interrupt this script call.
if(FirePreUserEvent(AI_FLAG_UDE_ON_DIALOGUE_PRE_EVENT, EVENT_ON_DIALOGUE_PRE_EVENT)) return;
// AI status check. Is the AI on?
if(GetAIOff()) return;
// Declarations
int nMatch = GetListenPatternNumber();
object oShouter = GetLastSpeaker();
string sSpoken = GetMatchedSubstring(0);
// We can ignore everything under special cases - EG no valid shouter,
// we are fleeing, its us, or we are not in the same area.
// - We break out of the script if this happens.
if(!GetIsObjectValid(oShouter) || /* Must be a valid speaker! */
oShouter == OBJECT_SELF || /* Not us! */
GetIsPerformingSpecialAction() || /* Not fleeing */
GetIgnore(oShouter) || /* Not ignoring the shouter */
GetArea(oShouter) != GetArea(OBJECT_SELF))/* Same area (Stops loud yellow shouts getting NPCs) */
{
// Fire End of Dialogue event
FireUserEvent(AI_FLAG_UDE_ON_DIALOGUE_EVENT, EVENT_ON_DIALOGUE_EVENT);
return;
}
// Conversation if not a shout.
if(nMatch == -1)
{
// * Don't speak when dead. 1.4 change (an obvious one to make)
if(CanSpeak())
{
// Make sure it is a PC and we are not fighting.
if(!GetIsFighting() && (GetIsPC(oShouter) || GetIsDMPossessed(oShouter)))
{
// If we have something random (or not) to say instead of
// the conversation, we will say that.
if(GetLocalInt(OBJECT_SELF, ARRAY_SIZE + AI_TALK_ON_CONVERSATION))
{
ClearAllActions();// Stop
SetFacingPoint(GetPosition(oShouter));// Face
SpeakArrayString(AI_TALK_ON_CONVERSATION);// Speak string
PlayAnimation(ANIMATION_LOOPING_TALK_NORMAL, 1.0, 3.0);// "Talk", then resume potitions.
ActionDoCommand(ExecuteScript(FILE_WALK_WAYPOINTS, OBJECT_SELF));
}
else
{
// If we are set to NOT clear all actions, we won't.
if(!GetSpawnInCondition(AI_FLAG_OTHER_NO_CLEAR_ACTIONS_BEFORE_CONVERSATION, AI_OTHER_MASTER))
{
ClearAllActions();
}
BeginConversation();
}
}
}
}
// If it is a valid shout...and a valid shouter.
// - Not a DM. Not ignoring shouting. Not a Debug String.
else if(!GetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID) &&// Not listening (IE heard already)
!GetIsDM(oShouter) && FindSubString(sSpoken, "[Debug]") == -1 &&
// 1.4 - Deafness (or they are seen) check, for fun.
(!GetHasEffect(EFFECT_TYPE_DEAF) || GetObjectSeen(oShouter)))
{
if(GetIsFriend(oShouter) || GetFactionEqual(oShouter))
{
// If they are a friend, not a PC, and a valid number, react.
// In the actual RespondToShout call, we do check to see if we bother.
// - Is PC - or is...master?
// - Shouts which are not negative, and not AI_ANYTHING_SAID_CONSTANT.
if(nMatch >= 0 && nMatch != AI_SHOUT_ANYTHING_SAID_CONSTANT &&
!GetIsPC(oShouter) && !GetIsPC(GetMaster(oShouter)))
{
// Respond to the shout
RespondToShout(oShouter, nMatch);
}
// Else either is PC or is shout 0 (everything!)
// - not if we are in combat, or they are not.
else if(!CannotPerformCombatRound() &&
GetIsInCombat(oShouter) &&
GetObjectType(oShouter) == OBJECT_TYPE_CREATURE)
{
// 57: "[Shout] Friend (may be PC) in combat. Attacking! [Friend] " + GetName(oShouter)
DebugActionSpeakByInt(57, oShouter);
// Respond to oShouter
IWasAttackedResponse(oShouter);
}
}
else if(GetIsEnemy(oShouter) && GetObjectType(oShouter) == OBJECT_TYPE_CREATURE)
{
// If we hear anything said by an enemy, and are not fighting, attack them!
if(!CannotPerformCombatRound())
// the negatives are associate shouts, Normally (!)
// 0+ are my shouts. 0 is anything
{
// We make sure it isn't an emote (set by default)
if(nMatch == AI_SHOUT_ANYTHING_SAID_CONSTANT &&
GetSpawnInCondition(AI_FLAG_OTHER_DONT_RESPOND_TO_EMOTES, AI_OTHER_MASTER))
{
// Jump out if its an emote - "*Nods*"
if(GetStringLeft(sSpoken, 1) == EMOTE_STAR &&
GetStringRight(sSpoken, 1) == EMOTE_STAR)
{
// Fire End of Dialogue event
FireUserEvent(AI_FLAG_UDE_ON_DIALOGUE_EVENT, EVENT_ON_DIALOGUE_EVENT);
return;
}
}
// 58: "[Shout] Responding to shout [Enemy] " + GetName(oShouter) + " Who has spoken!"
DebugActionSpeakByInt(58, oShouter);
// Short non-respond
SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, 6.0);
// Attack the enemy!
ClearAllActions();
DetermineCombatRound(oShouter);
// Shout to allies to attack the shouter
AISpeakString(AI_SHOUT_I_WAS_ATTACKED);
}
}
}
// Fire End of Dialogue event
FireUserEvent(AI_FLAG_UDE_ON_DIALOGUE_EVENT, EVENT_ON_DIALOGUE_EVENT);
}

View File

@@ -0,0 +1,241 @@
/*/////////////////////// [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);
}

View File

@@ -0,0 +1,164 @@
/*/////////////////////// [On Death] ///////////////////////////////////////////
Filename: J_AI_OnDeath or nw_c2_default7
///////////////////////// [On Death] ///////////////////////////////////////////
Speeded up no end, when compiling, with seperate Include.
Cleans up all un-droppable items, all ints and all local things when destroyed.
Check down near the bottom for a good place to add XP or corpse lines ;-)
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Added in Turn of corpses toggle
- Added in appropriate space for XP awards, marked with ideas (effect death)
1.4 - Removed the redudnant notes on the "You have gained 0 experience" message
///////////////////////// [Workings] ///////////////////////////////////////////
You can edit this for experience, there is a seperate section for it.
It will use DeathCheck to execute a cleanup-and-destroy script, that removes
any coprse, named "j_ai_destroyself".
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: GetLastKiller.
///////////////////////// [On Death] /////////////////////////////////////////*/
// We only require the constants/debug file. We have 1 function, not worth another include.
#include "J_INC_CONSTANTS"
// We need a wrapper. If the amount of deaths, got in this, is not equal to iDeaths,
// we don't execute the script, else we do. :-P
void DeathCheck(int nDeaths);
void main()
{
// If we are set to, don't fire this script at all
if(GetAIInteger(I_AM_TOTALLY_DEAD)) return;
// Pre-death-event. Returns TRUE if we interrupt this script call.
if(FirePreUserEvent(AI_FLAG_UDE_DEATH_PRE_EVENT, EVENT_DEATH_PRE_EVENT)) return;
// Note: No AI on/off check here.
// Who killed us? (alignment changing, debug, XP).
object oKiller = GetLastKiller();
// Stops if we just applied EffectDeath to ourselves.
if(GetLocalTimer(AI_TIMER_DEATH_EFFECT_DEATH)) return;
// Special: To stop giving out multiple amounts of XP, we use EffectDeath
// to change the killer, so the XP systems will NOT award MORE XP.
// - Even the default one suffers from this!
if(GetAIInteger(WE_HAVE_DIED_ONCE))
{
if(!GetLocalTimer(AI_TIMER_DEATH_EFFECT_DEATH))
{
// Don't apply effect death to self more then once per 2 seconds.
SetLocalTimer(AI_TIMER_DEATH_EFFECT_DEATH, 2.0);
// This should make the last killer us.
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectResurrection(), OBJECT_SELF);
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(GetMaxHitPoints()), OBJECT_SELF);
}
}
else if(oKiller != OBJECT_SELF)
{
// Set have died once, stops giving out mulitple amounts of XP.
SetAIInteger(WE_HAVE_DIED_ONCE, TRUE);
/*/////////////////////// [Experience] /////////////////////////////////////////
THIS is the place for it, below this comment.
It is useful to use GetFirstFactionMember (and Next), GiveXPToCreature,
GetXP, SetXP, GetChallengeRating (of self) all are really useful.
Bug note: GetFirstFactionMember/Next with the PC parameter means either ONLY PC,
and so NPC henchmen, unless FALSE is used, will not be even recognised.
///////////////////////// [Experience] ///////////////////////////////////////*/
// Do XP things (Use object "oKiller" for who killed us).
/*/////////////////////// [Experience] ///////////////////////////////////////*/
}
// Note: Here we do a simple way of checking how many times we have died.
// Nothing special. Debugging most useful aspect.
int nDeathCounterNew = GetAIInteger(AMOUNT_OF_DEATHS);
nDeathCounterNew++;
SetAIInteger(AMOUNT_OF_DEATHS, nDeathCounterNew);
// Here is the last time (in game seconds) we died. It is used in the executed script
// to make sure we don't prematurly remove areselves.
// We may want some sort of visual effect - like implosion or something, to fire.
int nDeathEffect = GetAIConstant(AI_DEATH_VISUAL_EFFECT);
// Valid constants from 0 and up. Apply to our location (not to us, who will go!)
if(nDeathEffect >= 0)
{
ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(nDeathEffect), GetLocation(OBJECT_SELF));
}
// Default Commoner alignment changing. (If the commoner is not evil!)
if(GetLevelByClass(CLASS_TYPE_COMMONER) > 0 &&
GetAlignmentGoodEvil(OBJECT_SELF) != ALIGNMENT_EVIL &&
!GetSpawnInCondition(AI_FLAG_OTHER_NO_COMMONER_ALIGNMENT_CHANGE, AI_OTHER_MASTER))
{
if(GetIsPC(oKiller))
{
AdjustAlignment(oKiller, ALIGNMENT_EVIL, 5);
}
else
{
// If it is a summon, henchmen or familar of a PC, we adust the PC itself
// Clever, eh?
object oMaster = GetMaster(oKiller);
if(GetIsObjectValid(oMaster) && GetIsPC(oMaster))
{
AdjustAlignment(oMaster, ALIGNMENT_EVIL, 5);
}
}
}
// Always shout when we are killed. Reactions - Morale penalty, and
// attack the killer.
AISpeakString(AI_SHOUT_I_WAS_KILLED);
// Speaks the set death speak, like "AGGGGGGGGGGGGGGGGGGG!! NOOOO!" for instance :-)
// Note for 1.4: No need for "CanSpeak()" for this, of course.
SpeakArrayString(AI_TALK_ON_DEATH);
// First check - do we use "destroyable corpses" or not? (default, yes)
if(!GetSpawnInCondition(AI_FLAG_OTHER_TURN_OFF_CORPSES, AI_OTHER_MASTER))
{
// We will actually dissapear after 30.0 seconds if not raised.
int nTime = GetAIInteger(AI_CORPSE_DESTROY_TIME);
if(nTime == 0) // Error checking
{
nTime = 30;
}
// 64: "[Death] Checking corpse status in " + IntToString(nTime) + " [Killer] " + GetName(oKiller) + " [Times Died Now] " + IntToString(nDeathCounterNew)
DebugActionSpeakByInt(64, oKiller, nTime, IntToString(nDeathCounterNew));
// Delay check
DelayCommand(IntToFloat(nTime), DeathCheck(nDeathCounterNew));
}
else
{
/************************ [Alternative Corpses] ********************************
This is where you can add some alternative corpse code - EG looting
and so on, without disrupting the rest of the AI (as the corpses
are turned off).
************************* [Alternative Corpses] *******************************/
// Add alternative corpse code here
/************************ [Alternative Corpses] *******************************/
}
// Signal the death event.
FireUserEvent(AI_FLAG_UDE_DEATH_EVENT, EVENT_DEATH_EVENT);
}
// We need a wrapper. If the amount of deaths, got in this, is not equal to iDeaths,
// we don't execute the script, else we do. :-P
void DeathCheck(int nDeaths)
{
// Do the deaths imputted equal the amount we have suffered?
if(GetAIInteger(AMOUNT_OF_DEATHS) == nDeaths)
{
// - This now includes a check for Bioware's lootable functions and using them.
ExecuteScript(FILE_DEATH_CLEANUP, OBJECT_SELF);
}
}

View File

@@ -0,0 +1,85 @@
/*/////////////////////// [On Disturbed] ///////////////////////////////////////
Filename: J_AI_OnDisturbed or nw_c2_default8
///////////////////////// [On Disturbed] ///////////////////////////////////////
This will attack pickpockets, and inventory disturbers. Note: Stupidly, Bioware
made this only affect the creature by stealing. Still, oh well :-(
This means that the only event which fires it is pickpocketing.
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Changed why we determine combat round
- Any change in inventory will trigger appropriate SetWeapons again.
- Added turn of hide things.
1.4 - Cleaned up a bit. Removed unused declared variable.
///////////////////////// [Workings] ///////////////////////////////////////////
Only fired by stealing, great. Oh well, it will attack any disturber anyway.
It *might* not be fired if the natural spot check to notice a theft doesn't
work. No idea personally.
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: GetInventoryDisturbItem, GetLastDisturbed,
GetInventoryDisturbType (I think it is always be stolen :-( ).
///////////////////////// [On Disturbed] /////////////////////////////////////*/
#include "J_INC_OTHER_AI"
void main()
{
// Pre-disturbed-event. Returns TRUE if we interrupt this script call.
if(FirePreUserEvent(AI_FLAG_UDE_DISTURBED_PRE_EVENT, EVENT_DISTURBED_PRE_EVENT)) return;
// AI status check. Is the AI on?
if(GetAIOff()) return;
// Declare major variables
object oDisturber = GetLastDisturbed();
object oItem = GetInventoryDisturbItem();
int nType = GetInventoryDisturbType();
int nBase = GetBaseItemType(oItem);
// We will reset weapons if it is a weapon.
// Reset weapons, or specifically healers kits.
if(GetIsObjectValid(oItem))
{
// Kits
if(nBase == BASE_ITEM_HEALERSKIT)
{
SetLocalInt(OBJECT_SELF, AI_WEAPONSETTING_SETWEAPONS, 2);
ExecuteScript(FILE_RE_SET_WEAPONS, OBJECT_SELF);
}
else // Think it is a weapon. Saves time :-)
{
SetLocalInt(OBJECT_SELF, AI_WEAPONSETTING_SETWEAPONS, 1);
ExecuteScript(FILE_RE_SET_WEAPONS, OBJECT_SELF);
}
}
// Fight! Or search!
if(!GetIgnoreNoFriend(oDisturber) &&
(nType == INVENTORY_DISTURB_TYPE_STOLEN || GetIsEnemy(oDisturber)))
{
// 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(oDisturber);
// Can we attack?
if(!CannotPerformCombatRound())
{
// Someone specific to attack
AISpeakString(AI_SHOUT_I_WAS_ATTACKED);
// One debug speak. We always do one.
// 65: "[Disturbed] (pickpocket) Attacking Enemy Distrube [Disturber] " + GetName(oTarget) + " [Type] " + IntToString(iType)
DebugActionSpeakByInt(65, oDisturber, nType);
// Attack the disturber
DetermineCombatRound(oDisturber);
}
else
{
// Get allies interested.
AISpeakString(AI_SHOUT_CALL_TO_ARMS);
}
}
// Fire End-heartbeat-UDE
FireUserEvent(AI_FLAG_UDE_DISTURBED_EVENT, EVENT_DISTURBED_EVENT);
}

View File

@@ -0,0 +1,114 @@
/*/////////////////////// [On Heartbeat] ///////////////////////////////////////
Filename: nw_c2_default1 or J_AI_OnHeartbeat
///////////////////////// [On Heartbeat] ///////////////////////////////////////
Removed stupid stuff, special behaviour, sleep.
Also, note please, I removed waypoints and day/night posting from this.
It can be re-added if you like, but it does reduce heartbeats.
Added in better checks to see if we should fire this script. Stops early if
some conditions (like we can't move, low AI settings) are set.
Hint: If nothing is used within this script, either remove it from creatures
or create one witch is blank, with just a "void main(){}" at the top.
Hint 2: You could add this very small file to your catche of scripts in the
module properties, as it runs on every creature every 6 seconds (ow!)
This also uses a system of Execute Script :-D This means the heartbeat, when
compiled, should be very tiny.
Note: NO Debug strings!
Note 2: Remember, I use default SoU Animations/normal animations. As it is
executed, we can check the prerequisists here, and then do it VIA
execute script.
-Working- Best possible, fast compile.
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Added more "buffs" to fast buff.
- Fixed animations (they both WORK and looping ones do loop right!)
- Loot behaviour!
- Randomly moving nearer a PC in 25M if set.
- Removed silly day/night optional setting. Anything we can remove, is a good idea.
1.4 - Removed AI level setting. Not good to use, I mistakenly added it.
///////////////////////// [Workings] ///////////////////////////////////////////
This fires off every 6 seconds (with PCs in the area, or AI_LEVEL_HIGH without)
and therefore is intensive.
It fires of ExecutesScript things for the different parts - saves CPU stuff
if the bits are not used.
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: Basically, none. Nothing activates this script. Fires every 6 seconds.
///////////////////////// [On Heartbeat] /////////////////////////////////////*/
// - This includes J_Inc_Constants
#include "J_INC_HEARTBEAT"
void main()
{
// Special - Runner from the leader shouts, each heartbeat, to others to get thier
// attention that they are being attacked.
// - Includes fleeing making sure (so it resets the ActionMoveTo each 6 seconds -
// this is not too bad)
// - Includes door bashing stop heartbeat
if(PerformSpecialAction()) return;
// Pre-heartbeat-event. Returns TRUE if we interrupt this script call.
if(FirePreUserEvent(AI_FLAG_UDE_HEARTBEAT_PRE_EVENT, EVENT_HEARTBEAT_PRE_EVENT)) return;
// AI status check. Is the AI on?
if(GetAIOff() || GetSpawnInCondition(AI_FLAG_OTHER_LAG_IGNORE_HEARTBEAT, AI_OTHER_MASTER)) return;
// Define the enemy and player to use.
object oEnemy = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY);
object oPlayer = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC);
// We can skip to the end if we are in combat, or something...
if(!JumpOutOfHeartBeat() && // We don't stop due to effects.
!GetIsInCombat() && // We are not in combat.
!GetIsObjectValid(GetAttackTarget()) && // Second combat check.
!GetObjectSeen(oEnemy)) // Nearest enemy is not seen.
{
// Fast buffing...if we have the spawn in condition...
if(GetSpawnInCondition(AI_FLAG_COMBAT_FLAG_FAST_BUFF_ENEMY, AI_COMBAT_MASTER) &&
GetIsObjectValid(oEnemy) && GetDistanceToObject(oEnemy) <= 40.0)
{
// ...we may do an advanced buff. If we cannot see/hear oEnemy, but oEnemy
// is within 40M, we cast many defensive spells instantly...
ExecuteScript(FILE_HEARTBEAT_TALENT_BUFF, OBJECT_SELF);
//...if TRUE (IE it does something) we turn of future calls.
DeleteSpawnInCondition(AI_FLAG_COMBAT_FLAG_FAST_BUFF_ENEMY, AI_COMBAT_MASTER);
// This MUST STOP the heartbeat event - else, the actions may be interrupted.
return;
}
// Execute waypoints file if we have waypoints set up.
if(GetWalkCondition(NW_WALK_FLAG_CONSTANT))
{
ExecuteScript(FILE_WALK_WAYPOINTS, OBJECT_SELF);
}
// We can't have any waypoints for the other things
else
{
// We must have animations set, and not be "paused", so doing a
// longer looping one
// - Need a valid player.
if(GetIsObjectValid(oPlayer) && !IsInConversation(OBJECT_SELF))
{
// We may search for PC enemies, 25% chance to move closer to PC's
if(GetSpawnInCondition(AI_FLAG_OTHER_SEARCH_IF_ENEMIES_NEAR, AI_OTHER_MASTER) &&
!GetLocalTimer(AI_TIMER_SEARCHING) && d4() == 1)
{
ExecuteScript(FILE_HEARTBEAT_WALK_TO_PC, OBJECT_SELF);
}
// Else, Do we have any animations to speak of?
// If we have a nearby PC, we do animations.
else if(GetHasValidAnimations())
{
ExecuteScript(FILE_HEARTBEAT_ANIMATIONS, OBJECT_SELF);
}
}
}
}
// Fire End-heartbeat-UDE
FireUserEvent(AI_FLAG_UDE_HEARTBEAT_EVENT, EVENT_HEARTBEAT_EVENT);
}

View File

@@ -0,0 +1,220 @@
/*/////////////////////// [On Percieve] ////////////////////////////////////////
Filename: J_AI_OnPercieve or nw_c2_default2
///////////////////////// [On Percieve] ////////////////////////////////////////
If the target is an enemy, attack
Will determine combat round on that person, is an enemy, basically.
Includes shouting for a big radius - if the spawn in condition is set to this.
NOTE: Debug strings in this file will be uncommented for speed by default.
- It is one of the most intensive scripts as it runs so often.
- Attempted to optimise as much as possible.
///////////////////////// [History] ////////////////////////////////////////////
1.3 - We include j_inc_other_ai to initiate combat (or go into combat again)
- j_inc_other_ai holds all other needed functions/integers ETC.
- Turn off hide things.
- Added "Only attack if attacked"
- Removed special conversation things. Almost no one uses them, and the taunt system is easier.
- Should now search around if they move to a dead body, and only once they get there.
1.4 - TO DO:
1. Perception needs checking - attacking outside perception ranges!
2. Vanishing targets, etc. test, improve.
3. Problems with dispelling invisibility. Maybe either do change the line to create placable, or, of course, cast at location (dispells cannot be metamagiked or whatever) Source
4. No Effect Type Ethereal. Source
///////////////////////// [Workings] ///////////////////////////////////////////
It fires:
- When a creature enters it perception range (Set in creature properties) and
is seen or heard.
* Tests show (and in general) it fires HEARD first, then immediantly SEEN if,
of course, they are visible. Odd really, but true.
- When a creature uses invisiblity/leaves the area in the creatures perception
range
- When a creature appears suddenly, already in the perception range (not
the other way round, normally)
- When a creature moves out of the creatures perception range, and therefore
becomes unseen.
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: GetLastPerceived, GetLastPerceptionSeen, GetLastPerceptionHeard,
GetLastPerceptionVanished, GetLastPerceptionInaudible.
///////////////////////// [On Percieve] //////////////////////////////////////*/
#include "J_INC_OTHER_AI"
void main()
{
// Pre-percieve-event. Returns TRUE if we interrupt this script call.
if(FirePreUserEvent(AI_FLAG_UDE_PERCIEVE_PRE_EVENT, EVENT_PERCIEVE_PRE_EVENT)) return;
// AI status check. Is the AI on?
if(GetAIOff()) return;
// Declare main things.
// - We declare OUTSIDE if's JUST IN CASE!
object oPerceived = GetLastPerceived();
object oAttackTarget = GetAttackTarget();
// 1.4: Very rarely is our attack target valid, so we will set it to
// what we would have melee attacked when DCR was called.
if(GetIgnoreNoFriend(oAttackTarget) || oAttackTarget == OBJECT_SELF)
{
oAttackTarget = GetAIObject(AI_LAST_MELEE_TARGET);
}
int bSeen = GetLastPerceptionSeen();
int bHeard = GetLastPerceptionHeard();
int bVanished = GetLastPerceptionVanished();
int bInaudiable = GetLastPerceptionInaudible();
// Debug
DebugActionSpeak("*** PER ***: " + GetName(oPerceived) + "| SEEN: " + IntToString(bSeen) +
"| HEARD: " + IntToString(bHeard) + "| VANISHED: " + IntToString(bVanished) +
"| INAUDIABLE: " + IntToString(bInaudiable));
// Need to be valid and not ignorable.
if(GetIsObjectValid(oPerceived) &&
!GetIsDM(oPerceived) &&
!GetIgnore(oPerceived))
{
// First, easy enemy checks.
if(GetIsEnemy(oPerceived) && !GetFactionEqual(oPerceived))
{
DebugActionSpeak("*** PER *** ENEMY");
// 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(oPerceived);
// Well, are we both inaudible and vanished?
// * the GetLastPerception should only say what specific event has fired!
if(bVanished || bInaudiable)
{
DebugActionSpeak("*** PER *** VANISHED OR INAUDIBLE");
// If they just became invisible because of the spell
// invisiblity, or improved invisiblity...we set a local object.
// - Beta: Added in ethereal as well.
if(GetHasEffect(EFFECT_TYPE_INVISIBILITY, oPerceived) ||
GetHasEffect(EFFECT_TYPE_SANCTUARY, oPerceived) ||
GetStealthMode(oPerceived) == STEALTH_MODE_ACTIVATED)
{
// Set object, AND the location they went invisible!
SetAIObject(AI_LAST_TO_GO_INVISIBLE, oPerceived);
// We also set thier location for AOE dispelling - same name
SetAILocation(AI_LAST_TO_GO_INVISIBLE, GetLocation(oPerceived));
}
// If they were our target, follow! >:-D
// - Optional, on spawn option, for following through areas.
if(oAttackTarget == oPerceived)
{
DebugActionSpeak("*** PER *** VANISHED OR INAUDIBLE AND IS CURRENT TARGET");
// This means they have exited the area! follow!
if(GetArea(oPerceived) != GetArea(OBJECT_SELF))
{
ClearAllActions();
// 51: "[Perception] Our Enemy Target changed areas. Stopping, moving too...and attack... [Percieved] " + GetName(oPerceived)
DebugActionSpeakByInt(51, oPerceived);
// Call to stop silly moving to enemies if we are fleeing
ActionMoveToEnemy(oPerceived);
}
// - Added check for not casting a spell. If we are, we finnish
// (EG: AOE spell) and automatically carry on.
// 1.4: If we are using a targetted spell, we do cancle our
// spellcasting if it is them.
else if(GetCurrentAction() != ACTION_CASTSPELL ||
GetAttackTarget() == oPerceived)
{
ClearAllActions();
// 52: "[Perception] Enemy Vanished (Same area) Retargeting/Searching [Percieved] " + GetName(oPerceived)
DebugActionSpeakByInt(52, oPerceived);
DetermineCombatRound(oPerceived);
}
}
}// End if just gone out of perception
// ELSE they have been SEEN or HEARD. We don't check specifics.
else //if(bSeen || bHeard)
{
// If they have been made seen, and they are our attack target,
// we must re-do combat round - unless we are casting a spell.
if(bSeen && GetCurrentAction() != ACTION_CASTSPELL &&
(oAttackTarget == oPerceived || !GetObjectSeen(oAttackTarget)))
{
// 53: "[Perception] Enemy seen, and was old enemy/cannot see current. Re-evaluating (no spell) [Percieved] " + GetName(oPerceived)
DebugActionSpeakByInt(53, oPerceived);
DetermineCombatRound(oPerceived);
// Shout to allies to attack oPerceived
AISpeakString(AI_SHOUT_I_WAS_ATTACKED);
}
// Else We check if we are already attacking.
else if(!CannotPerformCombatRound() &&
!GetSpawnInCondition(AI_FLAG_OTHER_ONLY_ATTACK_IF_ATTACKED, AI_OTHER_MASTER))
{
// * Don't speak when dead. 1.4 change (an obvious one to make)
if(CanSpeak())
{
// Special shout, d1000 though!
SpeakArrayString(AI_TALK_ON_PERCIEVE_ENEMY, TRUE);
}
// Stop stuff because of facing point - New enemy
HideOrClear();
// Get all allies in 60M to come to thier aid. Talkvolume silent
// shout does not seem to work well.
// - void function. Checks for the spawn int. in it.
// - Turns it off in it too (5 minutes - 1.4)
// - Variable range On Spawn
ShoutBossShout(oPerceived);
// 54: "[Perception] Enemy Seen. Not in combat, attacking. [Percieved] " + GetName(oPerceived)
DebugActionSpeakByInt(54, oPerceived);
// 1.4 change: SetFacingPoint(GetPosition(oPerceived));
// Is now part of DetermineCombatRound().
// * This means other events will work similarily.
DetermineCombatRound(oPerceived);
// Warn others
AISpeakString(AI_SHOUT_I_WAS_ATTACKED);
}
}
}
// Else, they are an equal faction, or not an enemy (or both)
else
{
// If they are dead, or fighting, eg: we saw something on
// waypoints, we charge in to investigate.
// * Higher intelligence will buff somewhat as well!
if((GetIsDead(oPerceived) || GetIsInCombat(oPerceived)) &&
(bSeen || bHeard))
{
if(GetIsDead(oPerceived))
{
// 55: "[Perception] Percieved Dead Friend! Moving into investigate [Percieved] " + GetName(oPerceived)
DebugActionSpeakByInt(55, oPerceived);
}
else
{
// 56: "[Perception] Percieved Alive Fighting Friend! Moving to and attacking. [Percieved] " + GetName(oPerceived)
DebugActionSpeakByInt(56, oPerceived);
}
// Check if we can attack
if(!CannotPerformCombatRound())
{
// Hide or clear actions
HideOrClear();
// If we were called to arms, react
CallToArmsResponse(oPerceived);
}
else
{
// Warn others even if we don't, or cannot, attack
AISpeakString(AI_SHOUT_CALL_TO_ARMS);
}
}
}
}
// Fire End of Percieve event
FireUserEvent(AI_FLAG_UDE_PERCIEVE_EVENT, EVENT_PERCIEVE_EVENT);
}

View File

@@ -0,0 +1,136 @@
/*/////////////////////// [On Phisical Attacked] ///////////////////////////////
Filename: J_AI_OnPhiAttack or nw_c2_default5
///////////////////////// [On Phisical Attacked] ///////////////////////////////
On Attacked
No checking for fleeing or warnings.
Very boring really!
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Added hiding things
1.4 - Missing Silent Shouts have been added.
///////////////////////// [Workings] ///////////////////////////////////////////
Got some simple Knockdown timer, so that we use heal sooner if we keep getting
a creature who is attempting to knock us down.
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: GetLastAttacker, GetLastWeaponUsed, GetLastAttackMode, GetLastAttackType
///////////////////////// [On Phisical Attacked] /////////////////////////////*/
#include "J_INC_OTHER_AI"
void main()
{
// Pre-attacked-event. Returns TRUE if we interrupt this script call.
if(FirePreUserEvent(AI_FLAG_UDE_ATTACK_PRE_EVENT, EVENT_ATTACK_PRE_EVENT)) return;
// AI status check. Is the AI on?
if(GetAIOff()) return;
// Set up objects.
object oAttacker = GetLastAttacker();
object oWeapon = GetLastWeaponUsed(oAttacker);
//int nMode = GetLastAttackMode(oAttacker); // Currently unused
int nAttackType = GetLastAttackType(oAttacker);
// Check if they are valid, a DM, we are ignoring them, they are dead now, or invalid
if(!GetIgnoreNoFriend(oAttacker))
{
// Adjust automatically if set.
if(GetSpawnInCondition(AI_FLAG_OTHER_CHANGE_FACTIONS_TO_HOSTILE_ON_ATTACK, AI_OTHER_MASTER))
{
if(!GetIsEnemy(oAttacker))
{
AdjustReputation(oAttacker, OBJECT_SELF, -100);
}
}
// If we were attacked by knockdown, for a timer of X seconds, we make
// sure we attempt healing at a higher level.
if(!GetLocalTimer(AI_TIMER_KNOCKDOWN) &&
(nAttackType == SPECIAL_ATTACK_IMPROVED_KNOCKDOWN ||
nAttackType == SPECIAL_ATTACK_KNOCKDOWN) &&
!GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_KNOCKDOWN) &&
GetBaseAttackBonus(oAttacker) + 20 >= GetAC(OBJECT_SELF))
{
SetLocalTimer(AI_TIMER_KNOCKDOWN, 30.0);
}
// Set last hostile, valid attacker (Used in the On Damaged event)
SetAIObject(AI_STORED_LAST_ATTACKER, oAttacker);
// * Don't speak when dead. 1.4 change (an obvious one to make)
if(CanSpeak())
{
// Speak the phisically attacked string, if applicable.
// Speak the damaged string, if applicable.
SpeakArrayString(AI_TALK_ON_PHISICALLY_ATTACKED);
}
// 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(oAttacker);
// We set if we are attacked in HTH onto a low-delay timer.
// - Not currently used
/*if(!GetLocalTimer(AI_TIMER_ATTACKED_IN_HTH))
{
// If the weapon is not ranged, or is not valid, set the timer.
if(!GetIsObjectValid(oWeapon) ||
!GetWeaponRanged(oWeapon))
{
SetLocalTimer(AI_TIMER_ATTACKED_IN_HTH, f18);
}
}*/
// If we are not fighting, and they are in the area, attack. Else, determine anyway.
if(!CannotPerformCombatRound())
{
// Must be in our area to go after now.
if(GetArea(oAttacker) == GetArea(OBJECT_SELF))
{
// 59: "[Phisically Attacked] Attacking back. [Attacker(enemy)] " + GetName(oAttacker)
DebugActionSpeakByInt(59, oAttacker);
// Attack the attacker
DetermineCombatRound(oAttacker);
// Shout to allies to attack the enemy who attacked me
AISpeakString(AI_SHOUT_I_WAS_ATTACKED);
}
else
{
// Shout to allies to attack, or be prepared.
AISpeakString(AI_SHOUT_CALL_TO_ARMS);
// 60: "[Phisically Attacked] Not same area. [Attacker(enemy)] " + GetName(oAttacker)
DebugActionSpeakByInt(60, oAttacker);
// May find another hostile to attack...
DetermineCombatRound();
}
}
else
{
// Shout to allies to attack, or be prepared.
AISpeakString(AI_SHOUT_CALL_TO_ARMS);
}
}
// Else, invalid, DM, ally, etc...must be prepared at least (could be
// they are charmed or something!)
else
{
// If we are not fighting, prepare for battle. Something bad must have
// happened.
if(!CannotPerformCombatRound())
{
// Respond to oAttacker as if they shouted for help.
IWasAttackedResponse(oAttacker);
}
else
{
// Shout to allies to attack, or be prepared.
AISpeakString(AI_SHOUT_CALL_TO_ARMS);
}
}
// Fire End of Attacked event
FireUserEvent(AI_FLAG_UDE_ATTACK_EVENT, EVENT_ATTACK_EVENT);
}

View File

@@ -0,0 +1,84 @@
/*/////////////////////// [On Rested] //////////////////////////////////////////
Filename: J_AI_OnRest or nw_c2_defaulta
///////////////////////// [On Rested] //////////////////////////////////////////
This will play the sitting animation for 6 seconds, just something for resting.
Also, walks waypoints (as resting would stop this) :-) and signals event (if so be)
Feel free to edit.
It does have the spell trigger information resetting, however. This can
only be removed if they have no spell triggers, although it is hardly worth it.
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Added sitting.
1.4 - Will be editing this down. No need to reset anything on rest, for a
better working AI.
IDEA: Change so that we will work through all spells/feats in order.
If, at cirtain levels, we do not have any spells to cast from that
level (set in a global stored integer in the general AI) we ignore all
spells in that level. Same for each talent category (no need to use
talents for them in the spawn script).
If not in combat (EG: In heartbeat) we reset the integers saying "don't
bother checking those spells" to false.
///////////////////////// [Workings] ///////////////////////////////////////////
This fires once, at the END of resting.
If ClearAllActions is added, the resting is actually stopped, or so it seems.
It doesn't fire more then once.
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: None, it seems.
///////////////////////// [On Rested] ////////////////////////////////////////*/
#include "J_INC_CONSTANTS"
// Resets all spell triggers used for sString
void LoopResetTriggers(string sString, object oTrigger);
void main()
{
// Pre-rest-event. Returns TRUE if we interrupt this script call.
if(FirePreUserEvent(AI_FLAG_UDE_RESTED_PRE_EVENT, EVENT_RESTED_PRE_EVENT)) return;
// AI status check. Is the AI on?
if(GetAIOff()) return;
// Simple debug.
// 66: "[Rested] Resting. Type: " + IntToString(GetLastRestEventType())
DebugActionSpeakByInt(66, OBJECT_INVALID, GetLastRestEventType());
// Reset all spell triggers.
// Set all triggers
object oTrigger = GetAIObject(AI_SPELL_TRIGGER_CREATURE);
if(GetIsObjectValid(oTrigger))
{
LoopResetTriggers(SPELLTRIGGER_NOT_GOT_FIRST_SPELL, oTrigger);
LoopResetTriggers(SPELLTRIGGER_DAMAGED_AT_PERCENT, oTrigger);
LoopResetTriggers(SPELLTRIGGER_IMMOBILE, oTrigger);
LoopResetTriggers(SPELLTRIGGER_START_OF_COMBAT, oTrigger);
}
// Some sitting for a few seconds.
ActionPlayAnimation(ANIMATION_LOOPING_SIT_CROSS, 1.0, 6.0);
DelayCommand(9.0, ExecuteScript(FILE_WALK_WAYPOINTS, OBJECT_SELF));
// Fire End-heartbeat-UDE
FireUserEvent(AI_FLAG_UDE_RESTED_EVENT, EVENT_RESTED_EVENT);
}
// Resets all spell triggers used for sString
void LoopResetTriggers(string sString, object oTrigger)
{
int nCnt, bBreak, bUsed;
for(nCnt = 1; bBreak != TRUE; nCnt++)
{
// Check max for this setting
bUsed = GetLocalInt(oTrigger, sString + USED);
if(bUsed)
{
DeleteLocalInt(oTrigger, sString + USED);
}
else
{
bBreak = TRUE;
}
}
}

View File

@@ -0,0 +1,644 @@
/*/////////////////////// [On Spawn] ///////////////////////////////////////////
Filename: J_AI_OnSpawn or nw_c2_default9
///////////////////////// [On Spawn] ///////////////////////////////////////////
This file contains options that will determine some AI behaviour, and a lot
of toggles for turning things on/off. A big read, but might be worthwhile.
The documentation is actually fully in the readme files, under the name
"On Spawn.html", under "AI File Explanations".
The order of the options:
- Important Spawn Settings N/A
- Targeting & Fleeing (AI_TARGETING_FLEE_MASTER)
- Fighting & Spells (AI_COMBAT_MASTER)
- Other Combat - Healing, Skills & Bosses (AI_OTHER_COMBAT_MASTER)
- Other - Death corpses, minor things (AI_OTHER_MASTER)
- User Defined (AI_UDE_MASTER)
- Shouts N/A
- Default Bioware settings (WP's, Anims) (NW_GENERIC_MASTER)
The OnSpawn file is a settings file. These things are set onto a creature, to
define cirtain actions. If more than one creature has this script, they all
use the settings, unless If/Else statements are used somehow. There is also
the process of setting any spells/feats availible, and hiding and walk waypoints
are started.
Other stuff:
- Targeting is imporant :-D
- If you delete this script, there is a template for the On Spawn file
in the zip it came in, for use in the "scripttemplate" directory.
///////////////////////// [History] ////////////////////////////////////////////
Note: I have removed:
- Default "Teleporting" and exit/return (this seemed bugged anyway, or useless)
- Spawn in animation. This can be, of course, re-added.
- Day/night posting. This is uneeded, with a changed walk waypoints that does it automatically.
1.0-1.2 - Used short amount of spawn options.
1.3 - All constants names are changed, I am afraid.
- Added Set/Delete/GetAIInteger/Constant/Object. This makes sure that the AI
doesn't ever interfere with other things - it pre-fixes all stored things
with AI_INTEGER_ (and so on)
1.4 - TO DO: Clear up some old non-working ones
- Added in User Defined part of the script, an auto-turn-off-spells for
Ranger and Paladin classes. Need to test - perhaps 1.64 fixed it?
Spawn options changed:
- Removed AI level settings (can still be done manually)
- Added optional (and off by default) fear-visual for fleeing
///////////////////////// [Workings] ///////////////////////////////////////////
Note: You can do without all the comments (it may be that you don't want
the extra KB it adds or something, although it does not at all slow down a module)
so as long as you have these at the end:
AI_SetUpEndOfSpawn();
DelayCommand(2.0, SpawnWalkWayPoints());
Oh, and the include file (Below, "j_inc_spawnin") must be at the top like
here. Also recommended is the AI_INTELLIGENCE and AI_MORALE being set (if
not using custom AI).
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: GetIsEncounterCreature
///////////////////////// [On Spawn] /////////////////////////////////////////*/
// Treasure Includes - See end of spawn for uncomment options.
//#include "nw_o2_coninclude"
// Uncomment this if you want default NwN Treasure - Uses line "GenerateNPCTreasure()" at the end of spawn.
// - This generates random things from the default pallet based on the creatures level + race
//#include "x0_i0_treasure"
// Uncomment this if you want the SoU Treasure - Uses line "CTG_GenerateNPCTreasure()" at the end of spawn.
// - This will spawn treasure based on chests placed in the module. See "x0_i0_treasure" for more information.
// This is required for all spawn in options!
#include "J_INC_SPAWNIN"
void main()
{
/************************ [Important Spawn Settings] **************************/
SetAIInteger(AI_INTELLIGENCE, 10);
// Intelligence value of the creauture. Can be 1-10, read readme's for help.
SetAIInteger(AI_MORALE, 10);
// Will save (See readme). Remember: -1 or below means they always flee.
//SetCustomAIFileName("CUSTOM_AI_FILE");
// Sets our custom AI file. Really, only animation settings will apply when this is set.
// - Can sort actions against a imputted target (EG: On Percieved enemy) by
// "GetLocalObject(OBJECT_SELF, "AI_TEMP_SET_TARGET");"
/************************ [Important Spawn Settings] **************************/
/************************ [Targeting] ******************************************
All targeting settings.
************************* [Targeting] *****************************************/
//SetSpawnInCondition(AI_FLAG_TARGETING_LIKE_LOWER_HP, AI_TARGETING_FLEE_MASTER);
// We only attack the lowest current HP.
//SetSpawnInCondition(AI_FLAG_TARGETING_LIKE_LOWER_AC, AI_TARGETING_FLEE_MASTER);
// We only attack the lowest AC (as in 1.2).
//SetSpawnInCondition(AI_FLAG_TARGETING_LIKE_LOWER_HD, AI_TARGETING_FLEE_MASTER);
// Target the lowest hit dice
//SetSpawnInCondition(AI_FLAG_TARGETING_LIKE_MAGE_CLASSES, AI_TARGETING_FLEE_MASTER);
// We go straight for mages/sorcerors. Nearest one.
//SetSpawnInCondition(AI_FLAG_TARGETING_LIKE_ARCHERS, AI_TARGETING_FLEE_MASTER);
// We go for the nearest enemy with a ranged weapon equipped.
//SetSpawnInCondition(AI_FLAG_TARGETING_LIKE_PCS, AI_TARGETING_FLEE_MASTER);
// We go for the nearest seen PC enemy.
//SetAIConstant(AI_FAVOURED_ENEMY_RACE, RACIAL_TYPE_HUMAN);
// The AI attacks the nearest enemy, seen, of this race. Use the RACIAL_* constants.
//SetAIConstant(AI_FAVOURED_ENEMY_CLASS, CLASS_TYPE_BARD);
// The AI attacks the nearest enemy, seen, of this class. Use the CLASS_* constants.
// Target changing - see readme for info.
//SetAIInteger(AI_MAX_TURNS_TO_ATTACK_ONE_TARGET, 6);
// Maximum rounds to attack the current target, before re-checking.
// % Chance to re-set each target type each round (Could result in current target still)
//SetAIInteger(AI_MELEE_LAST_TO_NEW_TARGET_CHANCE, 20);
//SetAIInteger(AI_RANGED_LAST_TO_NEW_TARGET_CHANCE, 20);
//SetAIInteger(AI_SPELL_LAST_TO_NEW_TARGET_CHANCE, 20);
// We only target PC's if there are any in range if this is set
//SetSpawnInCondition(AI_FLAG_TARGETING_FILTER_FOR_PC_TARGETS, AI_TARGETING_FLEE_MASTER);
// Main explanation of AI_SetAITargetingValues, see the AI readme (spawn file)
// - Remember, uncommenting one will just ignore it (so will never check target's
// AC without TARGETING_AC on)
AI_SetAITargetingValues(TARGETING_MANTALS, TARGET_LOWER, 1, 12);
// Spell mantals are checked only for the spell target. Either Absense of or got any.
AI_SetAITargetingValues(TARGETING_RANGE, TARGET_HIGHER, 2, 9);
// Range - very imporant! Basis for all ranged/spell attacks.
AI_SetAITargetingValues(TARGETING_AC, TARGET_LOWER, 2, 6);
// AC is used for all phisical attacks. Lower targets lower (By default).
AI_SetAITargetingValues(TARGETING_SAVES, TARGET_LOWER, 2, 4);
// Used for spell attacks. Saves are sorta a AC versus spells.
// Phisical protections. Used by spells, ranged and melee.
// Jasperre - simple check if we are a fighter (hit lower phisicals) or a
// mage (attack higher!)
if(GetBaseAttackBonus(OBJECT_SELF) > ((GetHitDice(OBJECT_SELF)/2) + 1))
{
// Fighter/Clerics (It is over a mages BAB + 1 (IE 0.5 BAB/Level) target lower
AI_SetAITargetingValues(TARGETING_PHISICALS, TARGET_LOWER, 2, 6);
}
else
{
// Mages target higher (so dispel/elemental attack those who fighters
// cannot hit as much). (the lowest BAB, under half our hit dice in BAB)
AI_SetAITargetingValues(TARGETING_PHISICALS, TARGET_HIGHER, 1, 5);
}
// Base attack bonus. Used for spells and phisical attacks. Checked with GetBaseAttackBonus.
AI_SetAITargetingValues(TARGETING_BAB, TARGET_LOWER, 1, 4);
// Hit dice - how powerful in levels the enemy is. Used for all checks.
AI_SetAITargetingValues(TARGETING_HITDICE, TARGET_LOWER, 1, 3);
//AI_SetAITargetingValues(TARGETING_HP_PERCENT, TARGET_LOWER, 1, 3);
//AI_SetAITargetingValues(TARGETING_HP_CURRENT, TARGET_LOWER, 1, 3);
//AI_SetAITargetingValues(TARGETING_HP_MAXIMUM, TARGET_LOWER, 1, 3);
// The HP's are the last thing to choose a target with.
/************************ [Targeting] *****************************************/
/************************ [Fleeing] ********************************************
Fleeing - these are toggled on/off by FEARLESS flag.
3 or under intelligence will just run away. 4 or more will know where allies
are, and if there are none, will not run.
************************* [Fleeing] *******************************************/
SetSpawnInCondition(AI_FLAG_FLEEING_FEARLESS, AI_TARGETING_FLEE_MASTER);
// Forces them to not flee. This may be set with AI_SetMaybeFearless at the end.
//SetSpawnInCondition(AI_FLAG_FLEEING_NEVER_FIGHT_IMPOSSIBLE_ODDS, AI_TARGETING_FLEE_MASTER);
// This will make the creature never fight against impossible odds (8HD+ different)
//SetSpawnInCondition(AI_FLAG_FLEEING_TURN_OFF_GROUP_MORALE, AI_TARGETING_FLEE_MASTER);
// This turns OFF any sort of group morale bonuses.
//SetAIInteger(AMOUNT_OF_HD_DIFFERENCE_TO_CHECK, -2);
// If enemy is within this amount of HD, we do not check morale.
//SetAIInteger(BASE_MORALE_SAVE, 20);
// Base DC of the will save. It is set to 20 + HD difference - Morale - Group morale mod.
//SetAIInteger(HP_PERCENT_TO_CHECK_AT, 80);
// %HP needed to be at to check morale. This doesn't affect "Never fight impossible odds"
//SetSpawnInCondition(AI_FLAG_FLEEING_NO_OVERRIDING_HP_AMOUNT, AI_TARGETING_FLEE_MASTER);
// This will turn off overriding HP checks. AI may decide to run even
// not at the %HP above, this turns the checks off.
//SetAIInteger(AI_DAMAGE_AT_ONCE_FOR_MORALE_PENALTY, GetMaxHitPoints()/6);
// Damage needed to be done at once to get a massive morale penalty (Below)
//SetAIInteger(AI_DAMAGE_AT_ONCE_PENALTY, 6);
// Penalty for the above, set for some time to negativly affect morale. Added to save DC for fleeing.
//SetSpawnInCondition(AI_FLAG_FLEEING_FLEE_TO_NEAREST_NONE_SEEN, AI_TARGETING_FLEE_MASTER);
// If set, just runs to nearest non-seen ally, and removes the loop for a good group of allies to run to.
//SetSpawnInCondition(AI_FLAG_FLEEING_FLEE_TO_OBJECT, AI_TARGETING_FLEE_MASTER);
// They will flee to the nearest object of the tag below, if set.
//SetLocalString(OBJECT_SELF, AI_FLEE_OBJECT, "BOSS_TAG_OR_WHATEVER");
// This needs setting if the above is to work.
//SetSpawnInCondition(AI_FLAG_FLEEING_USE_VISUAL_EFFECT, AI_TARGETING_FLEE_MASTER);
// If this is on, we play a visual effect while we flee.
/************************ [Fleeing] *******************************************/
/************************ [Combat - Fighters] **********************************
Fighter (Phiscal attacks, really) specific stuff - disarmed weapons, better
at hand to hand, and archer behaviour.
************************* [Combat - Fighters] *********************************/
SetSpawnInCondition(AI_FLAG_COMBAT_PICK_UP_DISARMED_WEAPONS, AI_COMBAT_MASTER);
// This sets to pick up weapons which are disarmed.
//SetAIInteger(AI_RANGED_WEAPON_RANGE, 3);
// This is the range at which they go into melee (from using a ranged weapon). Default is 3 or 5.
//SetSpawnInCondition(AI_FLAG_COMBAT_BETTER_AT_HAND_TO_HAND, AI_COMBAT_MASTER);
// Set if you want them to move forwards into HTH sooner. Will always
// if the enemy is a mage/archer, else % based on range.
//SetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ATTACKING, AI_COMBAT_MASTER);
// For archers. If they have ally support, they'd rather move back & shoot then go into HTH.
//SetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ALWAYS_MOVE_BACK, AI_COMBAT_MASTER);
// This forces the move back from attackers, and shoot bows. Very small chance to go melee.
//SetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ALWAYS_USE_BOW, AI_COMBAT_MASTER);
// This will make the creature ALWAYs use any bows it has. ALWAYS.
//SetSpawnInCondition(AI_FLAG_COMBAT_NO_GO_FOR_THE_KILL, AI_COMBAT_MASTER);
// Turns off any attempts to kill dying PCs, or attack low hit point people.
// This is only ever attempted at 9 or 10 intelligence anyway.
/************************ [Combat - Fighters] *********************************/
/************************ [Combat - Spell Casters] *****************************
Spellcaster AI has been improved significantly. As well as adding all new spells,
now spellcasters more randomly choose spells from the same level (EG: they
may choose not to cast magic missile, and cast negative energy ray instead).
There are also options here for counterspelling, fast buffing, Cheat cast spells,
dispelling, spell triggers, long ranged spells first, immunity toggles, and AOE settings.
************************* [Combat - Spell Casters] ****************************/
//SetSpawnInCondition(AI_FLAG_COMBAT_LONGER_RANGED_SPELLS_FIRST, AI_COMBAT_MASTER);
// Casts spells only if the caster would not move into range to cast them.
// IE long range spells, then medium, then short (unless the enemy comes to us!)
//SetSpawnInCondition(AI_FLAG_COMBAT_FLAG_FAST_BUFF_ENEMY, AI_COMBAT_MASTER);
// When an enemy comes in 40M, we fast-cast many defensive spells, as if prepared.
//SetSpawnInCondition(AI_FLAG_COMBAT_SUMMON_FAMILIAR, AI_COMBAT_MASTER);
// The caster summons thier familiar/animal companion. Either a nameless Bat or Badger respectivly.
// Counterspelling/Dispelling...
// It checks for these classes within the 20M counterspell range.
//SetSpawnInCondition(AI_FLAG_COMBAT_COUNTER_SPELL_ARCANE, AI_COMBAT_MASTER);
// If got dispels, it counterspells Arcane (Mage/Sorceror) spellcasters.
//SetSpawnInCondition(AI_FLAG_COMBAT_COUNTER_SPELL_DIVINE, AI_COMBAT_MASTER);
// If got dispels, it counterspells Divine (Cleric/Druid) spellcasters.
//SetSpawnInCondition(AI_FLAG_COMBAT_COUNTER_SPELL_ONLY_IN_GROUP, AI_COMBAT_MASTER);
// Recommended. Only counterspells with 5+ allies in group.
//SetSpawnInCondition(AI_FLAG_COMBAT_DISPEL_MAGES_MORE, AI_COMBAT_MASTER);
// Targets seen mages to dispel, else uses normal spell target.
SetSpawnInCondition(AI_FLAG_COMBAT_DISPEL_IN_ORDER, AI_COMBAT_MASTER);
// This will make the mage not dispel just anything all the time, but important (spell-stopping)
// things first, others later, after some spells. If off, anything is dispelled.
// AOE's
//SetSpawnInCondition(AI_FLAG_COMBAT_NEVER_HIT_ALLIES, AI_COMBAT_MASTER);
// Override toggle. Forces to never cast AOE's if it will hit an ally + harm them.
//SetSpawnInCondition(AI_FLAG_COMBAT_AOE_DONT_MIND_IF_THEY_SURVIVE, AI_COMBAT_MASTER);
// Allies who will survive the blast are ignored for calculating best target.
//SetAIInteger(AI_AOE_ALLIES_LOWEST_IN_AOE, 3);
// Defualt: 3. If amount of allies in blast radius are equal or more then
// this, then that location is ignored.
//SetAIInteger(AI_AOE_HD_DIFFERENCE, -8);
// Very weak allies (who are not comparable to us) are ignored if we would hit them.
// For these 2, if neither are set, the AI will choose AOE more if there are
// lots of enemies, or singles if there are not many.
//SetSpawnInCondition(AI_FLAG_COMBAT_SINGLE_TARGETING, AI_COMBAT_MASTER);
// For Same-level spells, single target spells are used first.
//SetSpawnInCondition(AI_FLAG_COMBAT_MANY_TARGETING, AI_COMBAT_MASTER);
// For Same-level spells, AOE spells are used first.
SetSpawnInCondition(AI_FLAG_COMBAT_IMPROVED_INSTANT_DEATH_SPELLS, AI_COMBAT_MASTER);
// A few Death spells may be cast top-prioritory if the enemy will always fail saves.
SetSpawnInCondition(AI_FLAG_COMBAT_IMPROVED_SUMMON_TARGETING, AI_COMBAT_MASTER);
// Will use a better target to summon a creature at (EG: Ranged attacker)
SetSpawnInCondition(AI_FLAG_COMBAT_IMPROVED_IMMUNITY_CHECKING, AI_COMBAT_MASTER);
// Turns On "GetIsImmune" checks. Auto on for 7+ Intel.
SetSpawnInCondition(AI_FLAG_COMBAT_IMPROVED_SPECIFIC_SPELL_IMMUNITY, AI_COMBAT_MASTER);
// Turns On checks for Globes & levels of spells. Auto on for 9+ Intel.
//SetSpawnInCondition(AI_FLAG_COMBAT_MORE_ALLY_BUFFING_SPELLS, AI_COMBAT_MASTER);
// This will make the caster buff more allies - or, in fact, use spells
// to buff allies which they might have not used before.
//SetSpawnInCondition(AI_FLAG_COMBAT_USE_ALL_POTIONS, AI_COMBAT_MASTER);
// Uses all buffing spells before melee.
//SetAICheatCastSpells(SPELL_MAGIC_MISSILE, SPELL_ICE_DAGGER, SPELL_HORIZIKAULS_BOOM, SPELL_MELFS_ACID_ARROW, SPELL_NEGATIVE_ENERGY_RAY, SPELL_FLAME_ARROW);
// Special: Mages cast for ever with this set.
// Spell triggers
//SetSpellTrigger(SPELLTRIGGER_NOT_GOT_FIRST_SPELL, FALSE, 1, SPELL_PREMONITION);
// This is just an example. See readme for more info.
/************************ [Combat - Spell Casters] ****************************/
/************************ [Combat - Dragons] ***********************************
I have a fondness for dragons - in NWN they are deprived of many abilities. Here
are some new ones for your enjoyment! Switches and flying for ANYTHING! :-)
************************* [Combat - Dragons] **********************************/
//SetSpawnInCondition(AI_FLAG_COMBAT_NO_WING_BUFFET, AI_COMBAT_MASTER);
//This sets so there is no Dragon wing buffet. Readme has details of it.
//SetAIInteger(AI_DRAGON_FREQUENCY_OF_BUFFET, 3);
// Min. Amount of Rounds between each buffet. See readme for counter defaults. Def: 3
//SetAIInteger(AI_DRAGON_FREQUENCY_OF_BREATH, 3);
// Min. Amount of Rounds between each breath use. See readme for counter defaults. Def: 3
// Default checks for dragon flying automatic turning on of flying.
if(GetLevelByClass(CLASS_TYPE_DRAGON) || GetRacialType(OBJECT_SELF) == RACIAL_TYPE_DRAGON)
{
SetSpawnInCondition(AI_FLAG_COMBAT_FLYING, AI_COMBAT_MASTER);
// This turns ON combat flying. I think anything winged looks A-OK. See readme for info.
}
/************************ [Combat - Dragons] **********************************/
/************************ [Combat Other - Healers/Healing] *********************
Healing behaviour - not specifically clerics. See readme.
************************* [Combat Other - Healers/Healing] ********************/
//SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_HEAL_AT_PERCENT_NOT_AMOUNT, AI_OTHER_COMBAT_MASTER);
// if this is set, we ignore the amount we need to be damaged, as long
// as we are under AI_HEALING_US_PERCENT.
//SetAIInteger(AI_HEALING_US_PERCENT, 50);
// % of HP we need to be at until we heal us at all. Default: 50
//SetAIInteger(AI_HEALING_ALLIES_PERCENT, 60);
// % of HP allies would need to be at to heal them Readme = info. Default: 60
SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_WILL_RAISE_ALLIES_IN_BATTLE, AI_OTHER_COMBAT_MASTER);
// Turns on rasing dead with Resurrection/Raise dead.
//SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_CURING, AI_OTHER_COMBAT_MASTER);
// This turns off all healing.
//SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_ONLY_CURE_SELF, AI_OTHER_COMBAT_MASTER);
// This turns off ally healing.
//SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_ONLY_RESTORE_SELF, AI_OTHER_COMBAT_MASTER);
// This turns off ally restoring (Remove/Restoration).
//SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_USE_BAD_HEALING_SPELLS, AI_OTHER_COMBAT_MASTER);
// This forces all cure spells to be used, check readme.
//SetAIInteger(SECONDS_BETWEEN_STATUS_CHECKS, 30);
// Seconds between when we loop everyone for bad effects like Fear/stun ETC. If not set, done each round.
//SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_GIVE_POTIONS_TO_HELP, AI_OTHER_COMBAT_MASTER);
// ActionGiveItem standard healing potion's to allies who need them, if they possess them.
/************************ [Combat Other - Healers/Healing] ********************/
/************************ [Combat Other - Skills] ******************************
Skills are a part of fighting - EG Taunt. These are mainly on/off switches.
A creature will *may* use it if they are not set to "NO_" for the skill.
************************* [Combat Other - Skills] *****************************/
// "NO" - This is for forcing the skill NEVER to be used by the combat AI.
// "FORCE" - This forces it on (and to be used), except if they have no got the skill.
//SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_PICKPOCKETING, AI_OTHER_COMBAT_MASTER);
//SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_PICKPOCKETING, AI_OTHER_COMBAT_MASTER);
//SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_TAUNTING, AI_OTHER_COMBAT_MASTER);
//SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_TAUNTING, AI_OTHER_COMBAT_MASTER);
//SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_EMPATHY, AI_OTHER_COMBAT_MASTER);
//SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_EMPATHY, AI_OTHER_COMBAT_MASTER);
//SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_HIDING, AI_OTHER_COMBAT_MASTER);
//SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_HIDING, AI_OTHER_COMBAT_MASTER);
//SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_OPENING_LOCKED_DOORS, AI_OTHER_COMBAT_MASTER);
//SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_OPENING_LOCKED_DOORS, AI_OTHER_COMBAT_MASTER);
//SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_USING_HEALING_KITS, AI_OTHER_COMBAT_MASTER);
//SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_PARRYING, AI_OTHER_COMBAT_MASTER);
//SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_PARRYING, AI_OTHER_COMBAT_MASTER);
//SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_SEARCH, AI_OTHER_COMBAT_MASTER);
//SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_SEARCH, AI_OTHER_COMBAT_MASTER);
// - Concentration - special notes in the readme
//SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_CONCENTRATION, AI_OTHER_COMBAT_MASTER);
//SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_CONCENTRATION, AI_OTHER_COMBAT_MASTER);
/************************ [Combat Other - Skills] *****************************/
/************************ [Combat Other - Leaders] *****************************
Leaders/Bosses can be set to issue some orders and inspire more morale - and bring
a lot of allies to a battle at once!
************************* [Combat Other - Leaders] ****************************/
//SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_GROUP_LEADER, AI_OTHER_COMBAT_MASTER);
// Special leader. Can issuse some orders. See readme for details.
//SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_BOSS_MONSTER_SHOUT, AI_OTHER_COMBAT_MASTER);
// Boss shout. 1 time use - calls all creatures in X meters (below) for battle!
//SetAIInteger(AI_BOSS_MONSTER_SHOUT_RANGE, 60);
// Defaults to a 60 M range. This can change it. Note: 1 toolset square = 10M.
/************************ [Combat Other - Leaders] ****************************/
/************************ [Other - Behaviour/Generic] **************************
This is generic behaviours - alright, really it is all things that cannot
really be categorised.
************************* [Other - Behaviour/Generic] *************************/
//SetSpawnInCondition(AI_FLAG_OTHER_NO_CLEAR_ACTIONS_BEFORE_CONVERSATION, AI_OTHER_MASTER);
// No ClearAllActions() before BeginConversation. May keep a creature sitting.
//SetSpawnInCondition(AI_FLAG_OTHER_NO_POLYMORPHING, AI_OTHER_MASTER);
// This will stop all polymorphing spells feats from being used.
//SetSpawnInCondition(AI_FLAG_OTHER_CHEAT_MORE_POTIONS, AI_OTHER_MASTER);
// If at low HP, and no potion, create one and use it.
//SetAIConstant(AI_POLYMORPH_INTO, POLYMORPH_TYPE_WEREWOLF);
// Polymorph to this creature when damaged (once, natural effect).
//AI_CreateRandomStats(-3, 3, 6);
// Create (Effect-applied) random statistics.
//AI_CreateRandomOther(-2, 2, -2, 2, -2, 2, -2, 2);
// Create (Effect-applied) random HP, saves, AC.
//SetSpawnInCondition(AI_FLAG_OTHER_RETURN_TO_SPAWN_LOCATION, AI_OTHER_MASTER);
// This will store our spawn location, and then move back there after combat.
SetSpawnInCondition(AI_FLAG_OTHER_DONT_RESPOND_TO_EMOTES, AI_OTHER_MASTER);
// This will ignore ALL chat by PC's (Enemies) who speak actions in Stars - *Bow*
//SetSpawnInCondition(AI_FLAG_OTHER_DONT_SHOUT, AI_OTHER_MASTER);
// Turns off all silent talking NPC's do to other NPC's.
//SetSpawnInCondition(AI_FLAG_OTHER_SEARCH_IF_ENEMIES_NEAR, AI_OTHER_MASTER);
// Move randomly closer to enemies in range set below.
//SetAIInteger(AI_SEARCH_IF_ENEMIES_NEAR_RANGE, 25);
// This is the range creatures use, in metres.
//SetSpawnInCondition(AI_FLAG_OTHER_ONLY_ATTACK_IF_ATTACKED, AI_OTHER_MASTER);
// One shot. We won't instantly attack a creature we see. See readme.
//SetAIInteger(AI_DOOR_INTELLIGENCE, 1);
// 3 Special "What to do with Doors" settings. See readme. Good for animals.
//SetSpawnInCondition(AI_FLAG_OTHER_REST_AFTER_COMBAT, AI_OTHER_MASTER);
// When combat is over, creature rests. Useful for replenising health.
//SetSpawnInCondition(AI_FLAG_OTHER_NO_PLAYING_VOICE_CHAT, AI_OTHER_MASTER);
// Stops any use of "PlayVoiceChat". Use with Custom speakstrings.
/*** Death settings - still under AI_OTHER_MASTER ***/
//AI_SetDeathResRef("Resref Here");
// Creates a creature from the string set. Instantly destroys this creatures body on death.
//SetAIConstant(AI_DEATH_VISUAL_EFFECT, VFX_FNF_IMPLOSION);
// Fires this visual effect number instantly on death. Use FNF and IMP ones.
//SetAIInteger(AI_CORPSE_DESTROY_TIME, 30);
// Seconds before body finally gets destroyed. Used for Clerical Raise Dead on NPC's.
//SetSpawnInCondition(AI_FLAG_OTHER_TURN_OFF_CORPSES, AI_OTHER_MASTER);
// This turns off the SetDestroyable() usually performed, and the above timer.
//SetSpawnInCondition(AI_FLAG_OTHER_USE_BIOWARE_LOOTING, AI_OTHER_MASTER);
// Makes the death file use Bioware's cool SetLootable() feature when corpses would disappear.
/*** Lag and a few performance settings - still under AI_OTHER_MASTER ***/
//SetSpawnInCondition(AI_FLAG_OTHER_LAG_NO_ITEMS, AI_OTHER_MASTER);
// The creature doesn't check for, or use any items that cast spells.
//SetSpawnInCondition(AI_FLAG_OTHER_LAG_NO_SPELLS, AI_OTHER_MASTER);
//The creature doesn't ever cast spells (and never checks them)
//SetSpawnInCondition(AI_FLAG_OTHER_LAG_NO_LISTENING, AI_OTHER_MASTER);
// The creature doesn't have SetListening() set. Turns of the basic listening for shouts.
//SetSpawnInCondition(AI_FLAG_OTHER_LAG_EQUIP_MOST_DAMAGING, AI_OTHER_MASTER);
// Uses EquipMostDamaging(), like Bioware code. No shield/second weapon equipped.
//SetSpawnInCondition(AI_FLAG_OTHER_LAG_NO_CURING_ALLIES, AI_OTHER_MASTER);
// This will stop checks for and curing of allies ailments.
//SetSpawnInCondition(AI_FLAG_OTHER_LAG_IGNORE_HEARTBEAT, AI_OTHER_MASTER);
// Stops the heartbeat running (Except Pre-Heartbeat-event).
//SetSpawnInCondition(AI_FLAG_OTHER_LAG_TARGET_NEAREST_ENEMY, AI_OTHER_MASTER);
// Ignores targeting settings. VERY good for lag/bad AI. Attacks nearest seen enemy.
/************************ [Other - Behaviour/Generic] *************************/
/************************ [User Defined and Shouts] ****************************
The user defined events, set up to fire here.
- New "Start combat attack" and "End Combat Attack" events
- New "Pre" events. Use these to optionally stop a script from firing
under cirtain circumstances as well! (Read nw_c2_defaultd or j_ai_onuserdef)
(User Defined Event = UDE)
************************* [User Defined and Shouts] ***************************/
// This is REQUIRED if we use any Pre-events. If not there, it will default
// to the default User Defined Event script for the default AI.
SetCustomUDEFileName("k_ai_onuserdef");
//SetSpawnInCondition(AI_FLAG_UDE_HEARTBEAT_EVENT, AI_UDE_MASTER); // UDE 1001
//SetSpawnInCondition(AI_FLAG_UDE_HEARTBEAT_PRE_EVENT, AI_UDE_MASTER); // UDE 1021
//SetSpawnInCondition(AI_FLAG_UDE_PERCIEVE_EVENT, AI_UDE_MASTER); // UDE 1002
//SetSpawnInCondition(AI_FLAG_UDE_PERCIEVE_PRE_EVENT, AI_UDE_MASTER); // UDE 1022
//SetSpawnInCondition(AI_FLAG_UDE_END_COMBAT_ROUND_EVENT, AI_UDE_MASTER); // UDE 1003
//SetSpawnInCondition(AI_FLAG_UDE_END_COMBAT_ROUND_PRE_EVENT, AI_UDE_MASTER); // UDE 1023
//SetSpawnInCondition(AI_FLAG_UDE_ON_DIALOGUE_EVENT, AI_UDE_MASTER); // UDE 1004
//SetSpawnInCondition(AI_FLAG_UDE_ON_DIALOGUE_PRE_EVENT, AI_UDE_MASTER); // UDE 1024
//SetSpawnInCondition(AI_FLAG_UDE_ATTACK_EVENT, AI_UDE_MASTER); // UDE 1005
//SetSpawnInCondition(AI_FLAG_UDE_ATTACK_PRE_EVENT, AI_UDE_MASTER); // UDE 1025
//SetSpawnInCondition(AI_FLAG_UDE_DAMAGED_EVENT, AI_UDE_MASTER); // UDE 1006
//SetSpawnInCondition(AI_FLAG_UDE_DAMAGED_PRE_EVENT, AI_UDE_MASTER); // UDE 1026
//SetSpawnInCondition(AI_FLAG_UDE_DEATH_EVENT, AI_UDE_MASTER); // UDE 1007
//SetSpawnInCondition(AI_FLAG_UDE_DEATH_PRE_EVENT, AI_UDE_MASTER); // UDE 1027
//SetSpawnInCondition(AI_FLAG_UDE_DISTURBED_EVENT, AI_UDE_MASTER); // UDE 1008
//SetSpawnInCondition(AI_FLAG_UDE_DISTURBED_PRE_EVENT, AI_UDE_MASTER); // UDE 1028
//SetSpawnInCondition(AI_FLAG_UDE_RESTED_EVENT, AI_UDE_MASTER); // UDE 1009
//SetSpawnInCondition(AI_FLAG_UDE_RESTED_PRE_EVENT, AI_UDE_MASTER); // UDE 1029
//SetSpawnInCondition(AI_FLAG_UDE_SPELL_CAST_AT_EVENT, AI_UDE_MASTER); // UDE 1011
//SetSpawnInCondition(AI_FLAG_UDE_SPELL_CAST_AT_PRE_EVENT, AI_UDE_MASTER); // UDE 1031
//SetSpawnInCondition(AI_FLAG_UDE_ON_BLOCKED_EVENT, AI_UDE_MASTER); // UDE 1015
//SetSpawnInCondition(AI_FLAG_UDE_ON_BLOCKED_PRE_EVENT, AI_UDE_MASTER); // UDE 1035
//SetSpawnInCondition(AI_FLAG_UDE_COMBAT_ACTION_EVENT, AI_UDE_MASTER); // UDE 1012
// Fires when we have finnished all combat actions.
//SetSpawnInCondition(AI_FLAG_UDE_COMBAT_ACTION_PRE_EVENT, AI_UDE_MASTER); // UDE 1032
// This fires at the start of DetermineCombatRound() *IF they can do an action*.
//SetSpawnInCondition(AI_FLAG_UDE_DAMAGED_AT_1_HP, AI_UDE_MASTER); // UDE 1014
// Fires when we are damaged, and are at 1 HP. Use for immortal-flagged creatures.
/*** Speakstrings - as it were, said under cirtain conditions % chance each time ***/
//AI_SetSpawnInSpeakArray(AI_TALK_ON_CONVERSATION, 100, 4, "Hello there", "I hope you enjoy your stay", "Do you work here too?", "*Hic*");
// On Conversation - see readme. Replaces BeginConversation().
// Morale
//AI_SetSpawnInSpeakArray(AI_TALK_ON_MORALE_BREAK, 100, 3, "No more!", "I'm outta here!", "Catch me if you can!");
// Spoken at running point, if they run to a group of allies.
//AI_SetSpawnInSpeakArray(AI_TALK_ON_CANNOT_RUN, 100, 3, "Never give up! Never surrender!", "I've no where to run, so make my day!", "RRRAAAAA!!!");
// Spoken at running point, if they can find no ally to run to, and 4+ Intelligence. See readme
//AI_SetSpawnInSpeakValue(AI_TALK_ON_STUPID_RUN, "Ahhhhgggg! NO MORE! Run!!");
// As above, when morale breaks + no ally, but they panic and run from enemy at 3 or less intelligence.
// Combat
//AI_SetSpawnInSpeakArray(AI_TALK_ON_COMBAT_ROUND_EQUAL, 5, 4, "Come on!", "You won't win!", "We are not equals! I am better!", "Nothing will stop me!");
//AI_SetSpawnInSpeakArray(AI_TALK_ON_COMBAT_ROUND_THEM_OVER_US, 5, 4, "I'll try! try! and try again!", "Tough man, are we?", "Trying out your 'skills'? Pathetic excuse!", "Nothing good will come from killing me!");
//AI_SetSpawnInSpeakArray(AI_TALK_ON_COMBAT_ROUND_US_OVER_THEM, 5, 4, "My strength is mighty then yours!", "You will definatly die!", "NO chance for you!", "No mercy! Not for YOU!");
// Spoken each DetermineCombatRound. % is /1000. See readme for Equal/Over/Under values.
//AI_SetSpawnInSpeakArray(AI_TALK_ON_TAUNT, 100, 3, "You're going down!", "No need to think, let my blade do it for you!", "Time to meet your death!");
// If the creature uses thier skill, taunt, on an enemy this will be said.
// Event-driven.
//AI_SetSpawnInSpeakArray(AI_TALK_ON_PERCIEVE_ENEMY, 70, 6, "Stand and fight, lawbreaker!", "Don't run from the law!", "I have my orders!", "I am ready for violence!", "CHARGE!", "Time you died!");
// This is said when they see/hear a new enemy, and start attacking them.
//AI_SetSpawnInSpeakArray(AI_TALK_ON_DAMAGED, 20, 2, "Ouch, damn you!", "Haha! Nothing will stop me!");
// A random value is set to speak when damaged, and may fire same time as below ones.
//AI_SetSpawnInSpeakArray(AI_TALK_ON_PHISICALLY_ATTACKED, 20, 2, "Hah! Mear weapons won't defeat me!", "Pah! You cannot defeat me with such rubbish!");
// This is said when an enemy attacks the creature with a melee/ranged weapon.
//AI_SetSpawnInSpeakArray(AI_TALK_ON_HOSTILE_SPELL_CAST_AT, 20, 2, "No one spell will stop me!", "Is that all you have!?!");
// This is said when an enemy attacks the creature with a hostile spell.
//AI_SetSpawnInSpeakValue(AI_TALK_ON_DEATH, "Agggggg!");
// This will ALWAYS be said, whenever the creature dies.
// Specific potion ones.
//AI_SetSpawnInSpeakValue(AI_TALK_WE_PASS_POTION, "Here! Catch!");
// This will be spoken when the creature passes a potion to an ally. See readme.
//AI_SetSpawnInSpeakValue(AI_TALK_WE_GOT_POTION, "Got it!");
// This will be spoken by the creature we pass the potion too, using AssignCommand().
// Leader ones
//AI_SetSpawnInSpeakValue(AI_TALK_ON_LEADER_SEND_RUNNER, "Quickly! We need help!");
// This will be said when the leader, if this creature, sends a runner.
//AI_SetSpawnInSpeakValue(AI_TALK_ON_LEADER_ATTACK_TARGET, "Help attack this target!");
// When the leader thinks target X should be attacked, it will say this.
//AI_SetSpawnInSpeakValue(AI_TALK_ON_LEADER_BOSS_SHOUT, "Come my minions! To battle!");
// This will be said when the leader, if this creature, sees an enemy and uses his "Boss Monster Shout", if turned on.
/************************ [User Defined and Shouts] ***************************/
/************************ [Bioware: Animations/Waypoints/Treasure] *************
All Bioware Stuff. I'd check out "x0_c2_spwn_def" for the SoU/Hordes revisions.
************************* [Bioware: Animations/Waypoints/Treasure] ************/
// SetSpawnInCondition(NW_FLAG_STEALTH, NW_GENERIC_MASTER);
// SetSpawnInCondition(NW_FLAG_SEARCH, NW_GENERIC_MASTER);
// Uses said skill while WalkWaypoints()
// SetSpawnInCondition(NW_FLAG_DAY_NIGHT_POSTING, NW_GENERIC_MASTER);
// Separate the NPC's waypoints into day & night. See comment in "nw_i0_generic" for use.
// SetSpawnInCondition(NW_FLAG_IMMOBILE_AMBIENT_ANIMATIONS, NW_GENERIC_MASTER);
// This will cause an NPC to use common animations it possesses,
// and use social ones to any other nearby friendly NPCs.
// SetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS, NW_GENERIC_MASTER);
// Same as above, except NPC will wander randomly around the area.
// SetAnimationCondition(NW_ANIM_FLAG_IS_CIVILIZED);
// Interacts with placeables + More civilized actions. See Readme.
// SetAnimationCondition(NW_ANIM_FLAG_CHATTER);
// Will use random voicechats during animations, if Civilized
// SetAnimationCondition(NW_ANIM_FLAG_IS_MOBILE_CLOSE_RANGE);
// Will move around the area a bit more, if using Immobile Animations. See readme.
// Treasure generating.
//CTG_GenerateNPCTreasure();
// SoU. Requires "x0_i0_treasure" to be uncommented. See readme.
//GenerateNPCTreasure();
// Default NwN. Requires "nw_o2_coninclude" to be uncommented. See readme.
/************************ [Bioware: Animations/Waypoints/Treasure] ************/
// AI Behaviour. DO NOT CHANGE! DO NOT CHANGE!!!
AI_SetUpEndOfSpawn();
// This MUST be called. It fires these events:
// SetUpSpells, SetUpSkillToUse, SetListeningPatterns, SetWeapons, AdvancedAuras.
// These MUST be called! the AI might fail to work correctly if they don't fire!
/************************ [User] ***********************************************
This is the ONLY place you should add user things, on spawn, such as
visual effects or anything, as it is after SetUpEndOfSpawn. By default, this
does have encounter animations on. This is here, so is easily changed :-D
Be careful otherwise.
Notes:
- SetListening is already set to TRUE, unless AI_FLAG_OTHER_LAG_NO_LISTENING is on.
- SetListenPattern's are set from 0 to 7.
- You can use the wrappers AI_SpawnInInstantVisual and AI_SpawnInPermamentVisual
for visual effects (Instant/Permament as appropriate).
************************* [User] **********************************************/
// Example (and default) of user addition:
// - If we are from an encounter, set mobile (move around) animations.
if(GetIsEncounterCreature())
{
SetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS, NW_GENERIC_MASTER);
}
// Leave this in if you use the variable for creature attacks, as for golems. Bioware's code.
int nNumber = GetLocalInt(OBJECT_SELF, "CREATURE_VAR_NUMBER_OF_ATTACKS");
if(nNumber > 0)
{
SetBaseAttackBonus(nNumber);
}
// If we are a ranger or paladin class, do not cast spells. This can be
// manually removed if wished. To get the spells they have working correctly,
// remove this, and use Monster Abilties instead of thier normal class spells.
// if(GetLevelByClass(CLASS_TYPE_RANGER) >= 1 || GetLevelByClass(CLASS_TYPE_PALADIN) >= 1)
// {
// SetSpawnInCondition(AI_FLAG_OTHER_LAG_NO_SPELLS, AI_OTHER_MASTER);
// }
/************************ [User] **********************************************/
// Note: You shouldn't really remove this, even if they have no waypoints.
DelayCommand(2.0, SpawnWalkWayPoints());
// Delayed walk waypoints, as to not upset instant combat spawning.
// This will also check if to change to day/night posts during the walking, no heartbeats.
}

View File

@@ -0,0 +1,493 @@
/*/////////////////////// [On Spell Cast At] ///////////////////////////////////
Filename: j_ai_onspellcast or nw_c2_defaultb
///////////////////////// [On Spell Cast At] ///////////////////////////////////
What does this do? Well...
- Any AOE spell effects are set in a timer, so we can react to them right
- Reacts to hostile casters, or allies in combat
And the normal attack :-)
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Added special AOE checks.
- Hide checks.
1.4 - Added more silent shouts. Edited the formatting. Moved a few things around.
///////////////////////// [Workings] ///////////////////////////////////////////
This is fired when EventSpellCastAt(object oCaster, int nSpell, int bHarmful=TRUE)
is signaled on the creature.
GetLastSpellCaster() = oCaster (Door, Placeable, Creature who cast it)
GetLastSpell() = nSpell (The spell cast at us)
GetLastSpellHarmful()= bHarmful (If it is harmful!)
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: GetLastSpellCaster, GetLastSpellHarmful GetLastSpell()
///////////////////////// [On Spell Cast At] /////////////////////////////////*/
#include "J_INC_OTHER_AI"
// Sets a local timer if the spell is an AOE one
void SetAOESpell(int nSpellCast, object oCaster);
// Gets the nearest AOE cast by oCaster, of sTag.
object GetNearestAOECastBy(string sTag, object oCaster);
// Gets the amount of protections we have - IE globes
int GetOurSpellLevelImmunity();
void main()
{
// Pre-spell cast at-event. Returns TRUE if we interrupt this script call.
if(FirePreUserEvent(AI_FLAG_UDE_SPELL_CAST_AT_PRE_EVENT, EVENT_SPELL_CAST_AT_PRE_EVENT)) return;
// AI status check. Is the AI on?
if(GetAIOff()) return;
object oCaster = GetLastSpellCaster();
int bHarmful = GetLastSpellHarmful();
int nSpellCast = GetLastSpell();
object oAttackerOfCaster;
// If harmful, we set the spell to a timer, if an AOE one.
if(bHarmful && GetIsObjectValid(oCaster))
{
// Might set AOE spell to cast.
SetAOESpell(nSpellCast, oCaster);
}
// If not a creature, probably an AOE or trap.
if(GetObjectType(oCaster) != OBJECT_TYPE_CREATURE)
{
// 67: "[Spell] Caster isn't a creature! May look for target [Caster] " + GetName(oCaster)
DebugActionSpeakByInt(67, oCaster);
// Shout to allies to attack, or be prepared.
AISpeakString(AI_SHOUT_CALL_TO_ARMS);
// Attack anyone else around
if(!CannotPerformCombatRound())
{
// Determine Combat Round
DetermineCombatRound();
}
}
// If a friend, or dead, or a DM, or invalid, or self, we ignore them.
else if(!GetIgnoreNoFriend(oCaster) && oCaster != OBJECT_SELF)
{
// 1.3 changes here:
// - We do NOT need to know if it is hostile or not, except if it is hostile
// and they are not our faction! We do, however, use bHarmful for speakstrings.
// If harmful, we attack anyone! (and if is enemy)
// 1.4: Faction equal check in GetIgnoreNoFriend()
if(bHarmful || GetIsEnemy(oCaster))
{
// Spawn in condition hostile thingy
if(GetSpawnInCondition(AI_FLAG_OTHER_CHANGE_FACTIONS_TO_HOSTILE_ON_ATTACK, AI_OTHER_MASTER))
{
if(!GetIsEnemy(oCaster))
{
AdjustReputation(oCaster, OBJECT_SELF, -100);
}
}
if(bHarmful)
{
// * Don't speak when dead. 1.4 change (an obvious one to make)
if(CanSpeak())
{
// Hostile spell speaksting, if set.
SpeakArrayString(AI_TALK_ON_HOSTILE_SPELL_CAST_AT);
}
}
// Turn of hiding check
TurnOffHiding(oCaster);
// We attack
if(!CannotPerformCombatRound())
{
// 68: "[Spell:Enemy/Hostile] Not in combat. Attacking: [Caster] " + GetName(oCaster)
DebugActionSpeakByInt(68, oCaster);
DetermineCombatRound(oCaster);
}
// Shout to allies to attack the enemy who attacked me, got via. Last Hostile Actor.
AISpeakString(AI_SHOUT_I_WAS_ATTACKED);
}
// Else, was neutral perhaps. Don't attack them anyway.
else
{
// 69: "[Spell] (ally). Not in combat. May Attack/Move [Caster] " + GetName(oCaster)
DebugActionSpeakByInt(69, oCaster);
// Set special action to investigate - as if this event was triggered
// by I_WAS_ATTACKED.
// If we are already attacking, we do not move
if(CannotPerformCombatRound())
{
// Shout to allies to attack, or be prepared.
AISpeakString(AI_SHOUT_CALL_TO_ARMS);
}
else
{
// We react as if the caster, a neutral, called for help ala
// I_WAS_ATTACKED (they might not have, might just be
// preperation for something), but normally, this is a neutral
// casting a spell. Do not respond to PC's.
if(!GetIsPC(oCaster))
{
IWasAttackedResponse(oCaster);
}
}
}
}
// If they are not a faction equal, and valid, we help them.
else if(GetIsObjectValid(oCaster) && GetFactionEqual(oCaster))
{
IWasAttackedResponse(oCaster);
}
// Fire End-spell cast at-UDE
FireUserEvent(AI_FLAG_UDE_SPELL_CAST_AT_EVENT, EVENT_SPELL_CAST_AT_EVENT);
}
// Sets a local timer if the spell is an AOE one
void SetAOESpell(int nSpellCast, object oCaster)
{
// Check it is one we can check
int bStop = TRUE;
switch(nSpellCast)
{
case SPELL_ACID_FOG:
case SPELL_MIND_FOG:
case SPELL_STORM_OF_VENGEANCE:
case SPELL_GREASE:
case SPELL_CREEPING_DOOM:
case SPELL_SILENCE:
case SPELL_BLADE_BARRIER:
case SPELL_CLOUDKILL:
case SPELL_STINKING_CLOUD:
case SPELL_WALL_OF_FIRE:
case SPELL_INCENDIARY_CLOUD:
case SPELL_ENTANGLE:
case SPELL_EVARDS_BLACK_TENTACLES:
case SPELL_CLOUD_OF_BEWILDERMENT:
case SPELL_STONEHOLD:
case SPELL_VINE_MINE:
case SPELL_SPIKE_GROWTH:
case SPELL_VINE_MINE_HAMPER_MOVEMENT:
case SPELL_VINE_MINE_ENTANGLE:
{
bStop = FALSE;
}
break;
}
// Check immune level
int nImmuneLevel = GetOurSpellLevelImmunity();
if(nImmuneLevel >= 9)
{
bStop = TRUE;
}
// Check
if(bStop == TRUE)
{
return;
}
// We do use intelligence here...
int nAIInt = GetBoundriedAIInteger(AI_INTELLIGENCE);
int bIgnoreSaves;
int bIgnoreImmunities;
object oAOE;
// If it is low, we ignore all things that we could ignore with it...
if(nAIInt <= 3)
{
bIgnoreSaves = TRUE;
bIgnoreImmunities = TRUE;
}
// Average ignores saves
else if(nAIInt <= 7)
{
bIgnoreSaves = TRUE;
bIgnoreImmunities = FALSE;
}
// Else, we do both.
else
{
bIgnoreSaves = FALSE;
bIgnoreImmunities = FALSE;
}
int bSetAOE = FALSE;// TRUE means set to timer
int nSaveDC = 11;
// Get the caster DC, the most out of WIS, INT or CHA...
int nInt = GetAbilityModifier(ABILITY_INTELLIGENCE, oCaster);
int nWis = GetAbilityModifier(ABILITY_WISDOM, oCaster);
int nCha = GetAbilityModifier(ABILITY_CHARISMA, oCaster);
if(nInt > nWis && nInt > nCha)
{
nSaveDC += nInt;
}
else if(nWis > nCha)
{
nSaveDC += nWis;
}
else
{
nSaveDC += nCha;
}
// Note:
// - No reaction type/friendly checks. Signal Event is only fired if the
// spell WILL pierce any PvP/Friendly/Area settings
// We check immunities here, please note...
switch(nSpellCast)
{
// First: IS GetIsReactionTypeHostile ones.
case SPELL_EVARDS_BLACK_TENTACLES:
// Fortitude save, but if we are immune to the hits, its impossible to hurt us
{
// If save immune OR AC immune, we ignore this.
if(25 >= GetAC(OBJECT_SELF) && nImmuneLevel < 4 &&
((GetFortitudeSavingThrow(OBJECT_SELF) < nSaveDC + 2) || bIgnoreSaves))
{
bSetAOE = TRUE;
// Nearest string of tag
oAOE = GetNearestAOECastBy(AI_AOE_PER_EVARDS_BLACK_TENTACLES, oCaster);
}
}
case SPELL_SPIKE_GROWTH:
case SPELL_VINE_MINE_HAMPER_MOVEMENT:
// d4 damage. LOTS of speed loss.
// Reflex save, or immunity, would stop the speed
{
if(nImmuneLevel < 3 &&
(!GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_MOVEMENT_SPEED_DECREASE) || bIgnoreImmunities) &&
((GetReflexSavingThrow(OBJECT_SELF) < nSaveDC + 5) || bIgnoreSaves))
{
bSetAOE = TRUE;
// Both use ENTANGLE AOE's
oAOE = GetNearestAOECastBy(AI_AOE_PER_ENTANGLE, oCaster);
}
}
break;
case SPELL_ENTANGLE:
case SPELL_VINE_MINE_ENTANGLE:
{
if(nImmuneLevel < 1 &&
(!GetHasFeat(FEAT_WOODLAND_STRIDE) || bIgnoreImmunities) &&
(!GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_ENTANGLE) || bIgnoreImmunities) &&
((GetReflexSavingThrow(OBJECT_SELF) < nSaveDC + 4) || bIgnoreSaves))
{
bSetAOE = TRUE;
// Both use ENTANGLE AOE's
oAOE = GetNearestAOECastBy(AI_AOE_PER_ENTANGLE, oCaster);
}
}
break;
case SPELL_WEB:
{
if(nImmuneLevel < 1 &&
(!GetHasFeat(FEAT_WOODLAND_STRIDE) || bIgnoreImmunities) &&
(!GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_ENTANGLE) || bIgnoreImmunities) &&
((GetReflexSavingThrow(OBJECT_SELF) < nSaveDC + 4) || bIgnoreSaves))
{
bSetAOE = TRUE;
// Web AOE
oAOE = GetNearestAOECastBy(AI_AOE_PER_WEB, oCaster);
}
}
break;
// Fort save
case SPELL_STINKING_CLOUD:
{
if(nImmuneLevel < 3 &&
(!GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_POISON) || bIgnoreImmunities) &&
(!GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_DAZED) || bIgnoreImmunities) &&
((GetFortitudeSavingThrow(OBJECT_SELF) < nSaveDC + 6) || bIgnoreSaves))
{
bSetAOE = TRUE;
// Stinking cloud persistant AOE.
oAOE = GetNearestAOECastBy(AI_AOE_PER_FOGSTINK, oCaster);
}
}
break;
// Fort save
case SPELL_CLOUD_OF_BEWILDERMENT:
{
if(nImmuneLevel < 2 &&
((GetFortitudeSavingThrow(OBJECT_SELF) < nSaveDC + 7) || bIgnoreSaves))
{
bSetAOE = TRUE;
// Bewilderment cloud persistant AOE.
oAOE = GetNearestAOECastBy(AI_AOE_PER_FOGBEWILDERMENT, oCaster);
}
}
break;
// Special: Mind save is the effect.
case SPELL_STONEHOLD:
{
if(nImmuneLevel < 6 &&
(!GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_MIND_SPELLS) || bIgnoreImmunities) &&
((GetWillSavingThrow(OBJECT_SELF) < nSaveDC + 7) || bIgnoreSaves))
{
bSetAOE = TRUE;
// Stonehold persistant AOE.
oAOE = GetNearestAOECastBy(AI_AOE_PER_STONEHOLD, oCaster);
}
}
break;
// Special: EFFECT_TYPE_SAVING_THROW_DECREASE is the effect.
case SPELL_MIND_FOG:
{
if(nImmuneLevel < 5 &&
(!GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_SAVING_THROW_DECREASE) || bIgnoreImmunities) &&
((GetWillSavingThrow(OBJECT_SELF) < nSaveDC + 6) || bIgnoreSaves))
{
bSetAOE = TRUE;
// Mind fog
oAOE = GetNearestAOECastBy(AI_AOE_PER_FOGMIND, oCaster);
}
}
break;
// Special: Feats, knockdown
case SPELL_GREASE:
{
if(nImmuneLevel < 1 &&
(!GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_KNOCKDOWN) || bIgnoreImmunities) &&
(!GetHasFeat(FEAT_WOODLAND_STRIDE, OBJECT_SELF) || bIgnoreImmunities) &&
((GetReflexSavingThrow(OBJECT_SELF) < nSaveDC + 2) || bIgnoreSaves))
{
bSetAOE = TRUE;
// Grease
oAOE = GetNearestAOECastBy(AI_AOE_PER_GREASE, oCaster);
}
}
break;
// All other ReactionType ones. Some have different saves though!
case SPELL_BLADE_BARRIER: // Reflex
case SPELL_INCENDIARY_CLOUD:// reflex
case SPELL_WALL_OF_FIRE:// Reflex
{
if(nImmuneLevel < 6 &&
(((GetReflexSavingThrow(OBJECT_SELF) < nSaveDC + 6) &&
!GetHasFeat(FEAT_IMPROVED_EVASION) &&
!GetHasFeat(FEAT_EVASION)) || bIgnoreSaves))
{
bSetAOE = TRUE;
if(nSpellCast == SPELL_BLADE_BARRIER)
{
// BB
oAOE = GetNearestAOECastBy(AI_AOE_PER_WALLBLADE, oCaster);
}
else if(nSpellCast == SPELL_INCENDIARY_CLOUD)
{
// Fog of fire
oAOE = GetNearestAOECastBy(AI_AOE_PER_FOGFIRE, oCaster);
}
else if(nSpellCast == SPELL_WALL_OF_FIRE)
{
// Wall of fire
oAOE = GetNearestAOECastBy(AI_AOE_PER_WALLFIRE, oCaster);
}
}
}
break;
case SPELL_ACID_FOG: // Fort: Half. No check, always damages.
case SPELL_CLOUDKILL:// No save!
case SPELL_CREEPING_DOOM: // No save!
{
if(nImmuneLevel < 6)
{
bSetAOE = TRUE;
if(nSpellCast == SPELL_ACID_FOG)
{
// Acid fog
oAOE = GetNearestAOECastBy(AI_AOE_PER_FOGACID, oCaster);
}
else if(nSpellCast == SPELL_CLOUDKILL)
{
// Cloud Kill
oAOE = GetNearestAOECastBy(AI_AOE_PER_FOGKILL, oCaster);
}
else if(nSpellCast == SPELL_CREEPING_DOOM)
{
// Creeping doom
oAOE = GetNearestAOECastBy(AI_AOE_PER_CREEPING_DOOM, oCaster);
}
}
}
// Storm - because the AI likes it, we stay in it if it is ours :-)
case SPELL_STORM_OF_VENGEANCE: // Reflex partial. No check, always damages.
{
if(oCaster != OBJECT_SELF && nImmuneLevel < 9)
{
bSetAOE = TRUE;
// Storm of vengance
oAOE = GetNearestAOECastBy(AI_AOE_PER_STORM, oCaster);
}
}
}
if(bSetAOE)
{
if(!GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(nSpellCast)))
{
SetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(nSpellCast), 18.0);
// Set nearest AOE
if(GetIsObjectValid(oAOE))
{
// Set nearest AOE of this spell to the local
object oNearest = GetAIObject(AI_TIMER_AOE_SPELL_EVENT + IntToString(nSpellCast));
if(GetDistanceToObject(oAOE) < GetDistanceToObject(oNearest) ||
!GetIsObjectValid(oNearest))
{
SetAIObject(AI_TIMER_AOE_SPELL_EVENT + IntToString(nSpellCast), oAOE);
}
}
}
}
}
// Gets the nearest AOE cast by oCaster, of sTag.
object GetNearestAOECastBy(string sTag, object oCaster)
{
int nCnt = 1;
object oAOE = GetNearestObjectByTag(sTag, OBJECT_SELF, nCnt);
object oReturn = OBJECT_INVALID;
// Loop
while(GetIsObjectValid(oAOE) && !GetIsObjectValid(oReturn))
{
// Check creator
if(GetAreaOfEffectCreator(oAOE) == oCaster)
{
oReturn = oAOE;
}
nCnt++;
oAOE = GetNearestObjectByTag(sTag, OBJECT_SELF, nCnt);
}
return oReturn;
}
// Gets the amount of protections we have - IE globes
int GetOurSpellLevelImmunity()
{
int nNatural = GetLocalInt(OBJECT_SELF, AI_SPELL_IMMUNE_LEVEL);
// Stop here, if natural is over 4
if(nNatural > 4) return nNatural;
// Big globe affects 4 or lower spells
if(GetHasSpellEffect(SPELL_GLOBE_OF_INVULNERABILITY, OBJECT_SELF) || nNatural >= 4)
return 4;
// Minor globe is 3 or under
if(GetHasSpellEffect(SPELL_MINOR_GLOBE_OF_INVULNERABILITY, OBJECT_SELF) ||
// Shadow con version
GetHasSpellEffect(SPELL_GREATER_SHADOW_CONJURATION_MINOR_GLOBE, OBJECT_SELF) ||
nNatural >= 3)
return 3;
// 2 and under is ethereal visage.
if(GetHasSpellEffect(SPELL_ETHEREAL_VISAGE, OBJECT_SELF) || nNatural >= 2)
return 2;
// Ghostly Visarge affects 1 or 0 level spells, and any spell immunity.
if(GetHasSpellEffect(SPELL_GHOSTLY_VISAGE, OBJECT_SELF) || nNatural >= 1 ||
// Or shadow con version.
GetHasSpellEffect(SPELL_GREATER_SHADOW_CONJURATION_MIRROR_IMAGE, OBJECT_SELF))
return 1;
// Return nNatural, which is 0-9
return FALSE;
}

View File

@@ -0,0 +1,326 @@
/*/////////////////////// [On User Defined] ////////////////////////////////////
Filename: J_AI_OnUserDef or nw_c2_defaultd
///////////////////////// [Workings] ///////////////////////////////////////////
1.4 Adds proper Pre-event functionality. Use this to make sure you keep the
AI working, but making small additions using this event.
Can be deleted to save space if you want :-)
How to use user defined events (brief):
There are a set of optional Spawn In values you can set within the spawn file.
If you set one of the Events to fire, it will activate this script. Then,
under the correct choice (EG I choose the Pre-Heartbeat event, then I
uncomment the line "SetSpawnInCondition(AI_FLAG_UDE_HEARTBEAT_PRE_EVENT, AI_UDE_MASTER);"
and find, in this file, the section with EVENT_HEARTBEAT_PRE_EVENT above it).
add in whatever to do.
With my Pre-heartbeat example, if I wanted it to check for a PC, then
check for a combat dummy, and attack it, I'd add this between the brackets:
// Code:
// Not in combat, of course!
if(!GetIsInCombat())
{
// Function in j_inc_npc_attack to get nearest PC
object oPC = GetNearestPCCreature();
// Why check for a PC? Well, it saves memory
if(GetIsObjectValid(oPC) && GetDistanceToObject(oPC) <= 40.0)
{
object oDummy = GetNearestObjectByTag("DUMMY");
if(GetIsObjectValid(oDummy))
{
ClearAllActions();
ActionAttack(oDummy);
// Exit (Stop the rest of the script)
SetToExitFromUDE(EVENT_HEARTBEAT_PRE_EVENT);
// Stop rest of script
return;
}
}
}
// End code
Simple, no?
You can delete sections you don't need, and is recommended as long as you know
how to use User Defined events.
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Added in with some documentation
1.4 - Changed Pre-events. Now, it uses Execute Script. Will need to set
a special string on the creature to now what script to fire.
- It means they work correctly, however!
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: Dependant on event. See seperate event scripts.
///////////////////////// [On User Defined] //////////////////////////////////*/
// This contains a lot of useful things.
// - Combat starting
// - Constant values
// - Get/Set spawn in values.
#include "J_INC_OTHER_AI"
// This contains some useful things to get NPC's to attack and so on.
#include "J_INC_NPC_ATTACK"
/************************ [UDE Values] *****************************************
These are uneeded, but here for reference. Use the constants in the file
"j_inc_constants" like "EVENT_HEARTBEAT_PRE_EVENT" which is classed as 1021.
* The normal death event might not fire before the creature has vanished.
Use the Pre-event (but with no stopping the death event) if you want a special
death event to happen.
Name Normal-End event - Pre-Event
Heartbeat Event 1001 1021
Percieve Event 1002 1022
Combat Round Event 1003 1023
Dialog Event 1004 1024
Attack Event 1005 1025
Damaged Event 1006 1026
Death Event 1007 1027
Disturbed Event 1008 1028
Rested Event 1009 1029
Spell Cast At Event 1011 1031
Combat Action Event 1012 1032
Damaged 1HP Event 1014 -
Blocked Event 1015 1035
************************* [UDE Values] ****************************************/
void main()
{
// Get the user defined number.
// * NOTE: YOU MUST USE AI_GetUDENumber(), not GetUserDefinedEventNumber()!
int nEvent = AI_GetUDENumber();
// Events.
switch(nEvent)
{
// Event Heartbeat
// Arguments: Basically, none. Nothing activates this script. Fires every 6 seconds.
case EVENT_HEARTBEAT_PRE_EVENT:
{
// This fires before the rest of the On Heartbeat file does
// Exit (Stop the rest of the script)
SetToExitFromUDE(EVENT_HEARTBEAT_PRE_EVENT);
}
break;
case EVENT_HEARTBEAT_EVENT:
{
// This fires after the rest of the On Heartbeat file does
}
break;
// Event Percieve
// Arguments: GetLastPerceived, GetLastPerceptionSeen, GetLastPerceptionHeard,
// GetLastPerceptionVanished, GetLastPerceptionInaudible.
case EVENT_PERCIEVE_PRE_EVENT:
{
// This fires before the rest of the On Percieve file does
// Exit (Stop the rest of the script)
SetToExitFromUDE(EVENT_PERCIEVE_PRE_EVENT);
}
break;
case EVENT_PERCIEVE_EVENT:
{
// This fires after the rest of the On Percieve file does
}
break;
// Event Combat Round End
// Arguments: GetAttackTarget, GetLastHostileActor, GetAttemptedAttackTarget,
// GetAttemptedSpellTarget (Or these are useful at least!)
case EVENT_END_COMBAT_ROUND_PRE_EVENT:
{
// This fires before the rest of the On Combat Round End file does
// Exit (Stop the rest of the script)
SetToExitFromUDE(EVENT_END_COMBAT_ROUND_PRE_EVENT);
}
break;
case EVENT_END_COMBAT_ROUND_EVENT:
{
// This fires after the rest of the On Combat Round End file does
}
break;
// Event Dialogue
// Arguments: GetListenPatternNumber, GetLastSpeaker, TestStringAgainstPattern,
// GetMatchedSubstring (I think),
case EVENT_ON_DIALOGUE_PRE_EVENT:
{
// This fires before the rest of the dialog file does
// Exit (Stop the rest of the script)
SetToExitFromUDE(EVENT_ON_DIALOGUE_PRE_EVENT);
}
break;
case EVENT_ON_DIALOGUE_EVENT:
{
// This fires after the rest of the dialog file does
}
break;
// Event Attacked
// Arguments: GetLastAttacker, GetLastWeaponUsed, GetLastAttackMode,
// GetLastAttackType
case EVENT_ATTACK_PRE_EVENT:
{
// This fires before the rest of the Attacked file does
// Exit (Stop the rest of the script)
SetToExitFromUDE(EVENT_ATTACK_PRE_EVENT);
}
break;
case EVENT_ATTACK_EVENT:
{
// This fires after the rest of the Attacked file does
}
break;
// Event Damaged
// Arguments: GetTotalDamageDealt, GetLastDamager, GetCurrentHitPoints
// (and max), GetDamageDealtByType
case EVENT_DAMAGED_PRE_EVENT:
{
// This fires before the rest of the damaged file does
// Exit (Stop the rest of the script)
SetToExitFromUDE(EVENT_DAMAGED_PRE_EVENT);
}
break;
case EVENT_DAMAGED_EVENT:
{
// This fires after the rest of the damaged file does
}
break;
// Event Death
// Arguments: GetLastKiller
case EVENT_DEATH_PRE_EVENT:
{
// This fires before the rest of the death file does
// Exit (Stop the rest of the script)
SetToExitFromUDE(EVENT_DEATH_PRE_EVENT);
}
break;
case EVENT_DEATH_EVENT:
{
// This fires after the rest of the death file does
}
break;
// Event Distrubed
// Arguments: GetInventoryDisturbItem, GetLastDisturbed,
// GetInventoryDisturbType (should always be stolen :-( ).
case EVENT_DISTURBED_PRE_EVENT:
{
// This fires before the rest of the disturbed file does
// Exit (Stop the rest of the script)
SetToExitFromUDE(EVENT_DISTURBED_PRE_EVENT);
}
break;
case EVENT_DISTURBED_EVENT:
{
// This fires after the rest of the disturbed file does
}
break;
// Event Rested
// Arguments: None
// Note: Not sure if this fires at the end of rest event, but the actual
// duration of the rest is 0, so you never "see" it.
case EVENT_RESTED_PRE_EVENT:
{
// This fires before the rest of the rested file does
// Exit (Stop the rest of the script)
SetToExitFromUDE(EVENT_RESTED_PRE_EVENT);
}
break;
case EVENT_RESTED_EVENT:
{
// This fires after the rest of the rested file does
}
break;
// Event Spell cast at
// Arguments: GetLastSpellCaster, GetLastSpellHarmful GetLastSpell()
case EVENT_SPELL_CAST_AT_PRE_EVENT:
{
// This fires before the rest of the Spell Cast At file does
// Exit (Stop the rest of the script)
SetToExitFromUDE(EVENT_SPELL_CAST_AT_PRE_EVENT);
}
break;
case EVENT_SPELL_CAST_AT_EVENT:
{
// This fires after the rest of the Spell Cast At End file does
}
break;
// Event Blocked
// Arguements: GetBlockingDoor, GetIsDoorActionPossible, GetLocked,
// GetLockKeyRequired, GetLockKeyTag, GetLockUnlockDC, GetPlotFlag.
case EVENT_ON_BLOCKED_PRE_EVENT:
{
// This fires before the rest of the on blocked file does
// Exit (Stop the rest of the script)
SetToExitFromUDE(EVENT_ON_BLOCKED_PRE_EVENT);
}
break;
case EVENT_ON_BLOCKED_EVENT:
{
// This fires after the rest of the on blocked file does
}
break;
// Event Combat Action
// Arguments: GetAttackTarget(), and lots of others.
// Note: Fires when DetermineCombatRound runs to perform an action.
case EVENT_COMBAT_ACTION_PRE_EVENT:
{
// This fires before the rest of the Determine Combat Round call does
// Exit (Stop the rest of the script)
SetToExitFromUDE(EVENT_COMBAT_ACTION_PRE_EVENT);
}
break;
case EVENT_COMBAT_ACTION_EVENT:
{
// This fires after the rest of the Determine Combat Round call does
// Calling ClearAllActions should stop any actions added in the call.
}
break;
// Event Damaged at 1 HP.
// Arguments: None really.
// Note: Fires OnDamaged, when we have exactly 1HP. Use for Immortal Creatures.
case EVENT_DAMAGED_AT_1_HP:
{
// This fires after the rest of the On Combat Round End file does
}
break;
// End all in-built events. Add more in here, if you wish.
}
}

View File

@@ -1,19 +1,21 @@
/************************ [Set Weapons] ****************************************
Filename: j_ai_setweapons
************************* [Set Weapons] ****************************************
/*/////////////////////// [Set Weapons] ////////////////////////////////////////
Filename: J_AI_SetWeapons
///////////////////////// [Set Weapons] ////////////////////////////////////////
Executed to re-set any weapons, or set them at spawn, using ExecuteScript.
It isn't included in the generic AI or onspawn to try and speed it up a little
************************* [History] ********************************************
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Added to a new file, removed from spawn include.
************************* [Workings] *******************************************
1.4 - Nothing changed here. Include file might have changed however.
///////////////////////// [Workings] ///////////////////////////////////////////
It can be easily re-added to the spawn include, however, the generic AI calls
it so little, that it may well be useful to keep a seperate file anyway.
************************* [Arguments] ******************************************
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: RESET_HEALING_KITS
************************* [Set Weapons] ***************************************/
///////////////////////// [Set Weapons] //////////////////////////////////////*/
#include "J_INC_SETWEAPONS"
#include "j_inc_setweapons"
void main()
{
if(GetAIInteger(RESET_HEALING_KITS))

View File

@@ -1,17 +1,18 @@
/************************ [Resume Waypoint Walking] ****************************
/*/////////////////////// [Resume Waypoint Walking] ////////////////////////////
Filename: j_ai_walkwaypoin
************************* [Resume Waypoint Walking] ****************************
///////////////////////// [Resume Waypoint Walking] ////////////////////////////
Executed On Spawn, and from the end of combat, to resume walking
Notes:
Needed my own file as to execute and be sure it exsisted. This means
the Non-override version will not use 2 different waypoint files most of the
time.
************************* [History] ********************************************
///////////////////////// [History] ////////////////////////////////////////////
1.0 - Added
1.3 - Changed to SoU waypoints. fired from End of Spawn and heartbeat.
It also returns to start location if set.
************************* [Workings] *******************************************
1.4 -
///////////////////////// [Workings] ///////////////////////////////////////////
Might change to SoU waypoints, this, at the moment, will just
walk normal waypoints.
@@ -48,18 +49,21 @@
Waypoints can be between areas and creatures will move there, if you set a
global integer variable called X2_SWITCH_CROSSAREA_WALKWAYPOINTS on your
module to 1.
************************* [Arguments] ******************************************
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: WAYPOINT_RUN, WAYPOINT_PAUSE are set On Spawn to remember
the pause/run actions.
************************* [Resume Waypoint Walking] ***************************/
///////////////////////// [Resume Waypoint Walking] //////////////////////////*/
#include "j_inc_debug"
#include "NW_I0_GENERIC"
#include "J_INC_DEBUG"
const string WAYPOINT_RUN = "WAYPOINT_RUN";
const string WAYPOINT_PAUSE = "WAYPOINT_PAUSE";
const int AI_FLAG_OTHER_RETURN_TO_SPAWN_LOCATION = 0x00020000;
const string AI_OTHER_MASTER = "AI_OTHER_MASTER";
const string AI_LOCATION = "AI_LOCATION";
const string AI_RETURN_TO_POINT = "AI_RETURN_TO_POINT";
// For return to.
int AI_GetSpawnInCondition(int nCondition, string sName, object oTarget = OBJECT_SELF);
@@ -69,7 +73,7 @@ void main()
// FIRST, if we are meant to move back to the start location, do it.
if(AI_GetSpawnInCondition(AI_FLAG_OTHER_RETURN_TO_SPAWN_LOCATION, AI_OTHER_MASTER))
{
location lReturnPoint = GetLocalLocation(OBJECT_SELF, "AI_RETURN_TO_POINT");
location lReturnPoint = GetLocalLocation(OBJECT_SELF, AI_LOCATION + AI_RETURN_TO_POINT);
object oReturnArea = GetAreaFromLocation(lReturnPoint);
if(GetIsObjectValid(oReturnArea))
{

View File

@@ -1,6 +1,6 @@
/************************ [Dragon Wing Buffet] *********************************
/*/////////////////////// [Ability - Dragon Wing Buffet] ///////////////////////
Filename: J_AI_WingBuffet
************************* [Dragon Wing Buffet] *********************************
///////////////////////// [Ability - Dragon Wing Buffet] ///////////////////////
"The dragon will launch into the air, knockdown
all opponents who fail a Reflex Save and then
land on one of those opponents doing damage
@@ -9,71 +9,90 @@
This is modified by Jasperre for use by Dragons in the AI. Instead of
crashing, using effect appear, disspear, it just uses effect appear.
************************* [History] ********************************************
Version 1.3 changes
- Made the "action attack" work better, getting the nearest
seen and heard instead of the nearest (which may not be seen or heard).
- Added in random damage for each target!
- Faction Equal as well as GetIsFriend check.
************************* [Workings] *******************************************
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Made the "action attack" work better, getting the nearest
seen and heard instead of the nearest (which may not be seen or heard).
- Added in random damage for each target!
- Faction Equal as well as GetIsFriend check.
1.4 - Cleaned it up a bit, to be more readable.
///////////////////////// [Workings] ///////////////////////////////////////////
Executed via. ExecuteScript from the AI file, it is seperate because it is
almost a new AI ability.
************************* [Arguments] ******************************************
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: N/A
************************* [Dragon Wing Buffet] ********************************/
///////////////////////// [Ability - Dragon Wing Buffet] /////////////////////*/
void main()
{
//Declare major variables
// Stop the creature's actions.
ClearAllActions();// To rid errors.
effect eKnockDown = EffectKnockdown();
// Declare major variables
int nDamage;
int nDC = GetHitDice(OBJECT_SELF);
effect eDam;
effect eVis = EffectVisualEffect(VFX_IMP_PULSE_WIND);
// Use a delay based on range,
float fDelay;
location lSelf = GetLocation(OBJECT_SELF);
string sMessage = GetName(OBJECT_SELF) + " is using its wing buffet against you!";
location lTarget;
float fRandomKnockdown;
// Use a delay based on range,
float fDelay;
// Declare effects
effect eDam;
effect eKnockDown = EffectKnockdown();
effect eVis = EffectVisualEffect(VFX_IMP_PULSE_WIND);
// Pulse of wind applied...
ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eVis, lSelf);
//Apply the VFX impact and effects
//Get first target in spell area
// Get first target in spell area
object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_GARGANTUAN, lSelf);
while(GetIsObjectValid(oTarget))
{
// Get thier location (for visual) and the delay.
lTarget = GetLocation(oTarget);
fDelay = GetDistanceToObject(oTarget)/20.0;
// Wind pulse to all
DelayCommand(fDelay, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eVis, lTarget));
//Get next target in spell area
// Do not effect allies.
if(!GetIsFriend(oTarget) && !GetFactionEqual(oTarget))
{
// Send a message about the wing buffet (allies do not see this)
SendMessageToPC(oTarget, sMessage);
// Huge creatures (IE: Dragon size) are not affected.
if(GetCreatureSize(oTarget) != CREATURE_SIZE_HUGE)
{
// A standard (not spell) reflex save negates the damage and knockdown
if(!ReflexSave(oTarget, nDC))
{
// Randomise damage. (nDC = Hit dice)
nDamage = Random(nDC) + 11;
// Define the damage
eDam = EffectDamage(nDamage, DAMAGE_TYPE_BLUDGEONING);
// Randomise knockdown, to minimum of 6.0 (1.0/2 = 0.5. + 5.5 = 6.0)
// Randomise knockdown duration, to minimum of 6.0 (1.0/2 = 0.5. + 5.5 = 6.0)
fRandomKnockdown = 5.5 + ((IntToFloat(Random(30)) + 1.0)/10.0);
// We'll have a windy effect..depending on range
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget));
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eKnockDown, oTarget, fRandomKnockdown));
}
}
}
// Get next target in spell area
oTarget = GetNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_GARGANTUAN, lSelf);
}
// Do a great flapping wings on land effect.
effect eAppear = EffectAppear();
ApplyEffectToObject(DURATION_TYPE_INSTANT, eAppear, OBJECT_SELF);
// Attack the nearest seen (so not to stand there for 6 seconds).
// Attack the nearest seen (so not to stand there for 6 seconds, but get
// back in the action!).
object oNearest = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE);
if(GetIsObjectValid(oNearest))
{

View File

@@ -1,6 +1,6 @@
/************************ [Dragon Flying] **************************************
/*/////////////////////// [Ability - Dragon Flying] ////////////////////////////
Filename: J_AI_WingFlying
************************* [Dragon Flying] **************************************
///////////////////////// [Ability - Dragon Flying] ////////////////////////////
Hey, a dragon can fly (if we are set to, mind you!) this is executed from
the default AI, using local objects to "fly" to, a duration based on the
distance between the 2 places.
@@ -13,10 +13,11 @@
- Can be used with NPC's who are not dragons, but if they are not huge,
the damage is not done (only the pulses at thier location and the target
location)
************************* [History] ********************************************
Version 1.3
- Added
************************* [Workings] *******************************************
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Added
1.4 - Added an actual spell event fire for the damage. It might not have
registered with some hostile monsters otherwise! (EG: DR)
///////////////////////// [Workings] ///////////////////////////////////////////
Executed via. ExecuteScript from the AI file, it is seperate because it is
almost a new AI ability.
@@ -24,11 +25,11 @@
1M between targets. Not too much, but enough.
Does damage to landing and taking off sites too :-)
************************* [Arguments] ******************************************
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: N/A
************************* [Dragon Flying] *************************************/
///////////////////////// [Ability - Dragon Flying] //////////////////////////*/
#include "j_inc_constants"
#include "J_INC_CONSTANTS"
// Damages area with blast of flying
void DoDamageToArea(location lLocation);
@@ -55,7 +56,7 @@ void main()
if(GetCreatureSize(OBJECT_SELF) == CREATURE_SIZE_HUGE)
{
// Damage instantly
DelayCommand(f1, DoDamageToArea(lSelf));
DelayCommand(1.0, DoDamageToArea(lSelf));
// Delay the jump down damage - a little extra delay mind you.
DelayCommand(fDuration + 1.2, DoDamageToArea(lJumpTo));
}
@@ -64,7 +65,7 @@ void main()
// Visual effects only
effect eImpact = EffectVisualEffect(VFX_IMP_PULSE_WIND);
// Pulse of wind applied...
DelayCommand(f1, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eImpact, lSelf));
DelayCommand(1.0, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eImpact, lSelf));
// Delay the new wind
DelayCommand(fDuration + 1.2, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eImpact, lJumpTo));
}
@@ -72,7 +73,7 @@ void main()
DelayCommand(fDuration + 1.5, ActionAttack(oJumpTo));
effect eFly = EffectDisappearAppear(lJumpTo);
DelayCommand(f1, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFly, OBJECT_SELF, fDuration - f1));
DelayCommand(1.0, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFly, OBJECT_SELF, fDuration - 1.0));
}
// Damages area with blast of flying
@@ -109,6 +110,10 @@ void DoDamageToArea(location lLocation)
// Can't knock over huge things!
if(GetCreatureSize(oTarget) != CREATURE_SIZE_HUGE)
{
// Signal spell cast at event
// * Using: SPELLABILITY_DRAGON_WING_BUFFET - just so something is used
SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, SPELLABILITY_DRAGON_WING_BUFFET));
// Reflex save for damage
if(!ReflexSave(oTarget, nDC))
{

File diff suppressed because it is too large Load Diff

View File

@@ -1,28 +1,27 @@
/************************ [Debug] **********************************************
/*/////////////////////// [Include - Debugging] ////////////////////////////////
Filename: J_Inc_Debug
************************* [Debug] **********************************************
///////////////////////// [Include - Debugging] ////////////////////////////////
This contains DebugActionSpeak, the debug function.
Makes it easier to uncomment debug lines.
************************* [History] ********************************************
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Added
************************* [Workings] *******************************************
- In beta, changed it so this file controls all debug strings. Just
uncomment them and recompile to turn it on/off.
1.4 - TO DO: Added some more debug strings I use
///////////////////////// [Workings] ///////////////////////////////////////////
DebugActionSpeak normally writes a timestamped log entry, and speak a silent
string Server Admins can hear.
To Do: Might make it more generic debug lines, where you can uncomment all
"XX" lines HERE, not in the files, so it compiles without them, and only
need an integer to speak one.
1.3 added:
- DebugActionSpeakByInt(int iInteger);
- Removes many strings into this file
- Can easily comment out all string so they are not added to compiled
scripts if debugging unused (This saves space on compiled files :-D )
- Always uncomment the right bits if not using any debugging.
************************* [Arguments] ******************************************
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: N/A
************************* [Debug] *********************************************/
///////////////////////// [Include - Debugging] //////////////////////////////*/
// This will speak a cirtain integer number string (similar to a dialog reference).
// - I (Jass) have just moved all strings I used all the time into here, so
@@ -30,7 +29,7 @@
// - The numbers have no reference to much really.
// - Calls DebugActionSpeak!
// - See J_INC_DEBUG to uncomment/recomment in
void DebugActionSpeakByInt(int iInteger, object oInput = OBJECT_INVALID, int iInput = FALSE, string sInput = "");
void DebugActionSpeakByInt(int iInteger, object oInput = OBJECT_INVALID, int nInput = FALSE, string sInput = "");
// Speaks and stamps a debug string.
// - See J_INC_DEBUG to uncomment/recomment the debug strings.
@@ -43,13 +42,13 @@ void DebugActionSpeak(string sString);
// - The numbers have no reference to much really.
// - Calls DebugActionSpeak!
// - See J_INC_DEBUG to uncomment/recomment in
void DebugActionSpeakByInt(int iInteger, object oInput = OBJECT_INVALID, int iInput = FALSE, string sInput = "")
void DebugActionSpeakByInt(int iInteger, object oInput = OBJECT_INVALID, int nInput = FALSE, string sInput = "")
{
// TO UNCOMMENT/COMMENT:
// - Add/Remove in "//" before the next lines "/*"
// - Recompile all files
/*
///*
string sDebug;
switch(iInteger)
@@ -58,22 +57,22 @@ void DebugActionSpeakByInt(int iInteger, object oInput = OBJECT_INVALID, int iIn
case 1: sDebug = "[DCR:Melee] Most Damaging Weapon. Target: " + GetName(oInput); break;
case 2: sDebug = "[DCR:Melee] Most Damaging as Not Effective"; break;
case 3: sDebug = "[DCR:Melee] Melee Code. No valid melee target/Dead. Exiting"; break;
case 4: sDebug = "[DCR:Melee] Melee attack. [Target] " + GetName(oInput) + " [Feat/Attack] " + IntToString(iInput); break;
case 4: sDebug = "[DCR:Melee] Melee attack. [Target] " + GetName(oInput) + " [Feat/Attack] " + IntToString(nInput); break;
case 5: sDebug = "[DCR:Caster] Defensive Casting Mode ON [Enemy] " + GetName(oInput); break;
case 6: sDebug = "[DCR:Caster] Moving away from AOO's. [Enemy] " + GetName(oInput); break;
case 7: sDebug = "[DCR:Casting] Talent(item) [TalentID] " + IntToString(iInput) + " [Target] " + GetName(oInput) + " [Location] " + sInput; break;
case 8: sDebug = "[DCR:Casting] Workaround for Spontaeous [SpellID] " + IntToString(iInput) + " [Target] " + GetName(oInput); break;
case 9: sDebug = "[DCR:Casting] NormalSpell [ID] " + IntToString(iInput) + " [Target] " + GetName(oInput) + " [Location] " + sInput; break;
case 10: sDebug = "[DCR:Casting] TalentSpell. [ID] " + IntToString(iInput) + " [Target] " + GetName(oInput) + " [Location] " + sInput; break;
case 11: sDebug = "[DCR:Casting] SubSpecialSpell. [ID] " + IntToString(iInput) + " [Target] " + GetName(oInput) + " [Location] " + sInput; break;
case 12: sDebug = "[DCR:Casting] NormalRandomSpell. [ID] " + IntToString(iInput) + " [Target] " + GetName(oInput) + " [Location] " + sInput; break;
case 13: sDebug = "[DCR:Casting] Backup spell caught: " + IntToString(iInput); break;
case 14: sDebug = "[DCR:Feat] [ID] " + IntToString(iInput) + " [Enemy] " + GetName(oInput); break;
case 15: sDebug = "[DCR:Casting] Grenade [ID] " + IntToString(iInput) + " [Target] " + GetName(oInput) + " [Location] " + sInput; break;
case 7: sDebug = "[DCR:Casting] Talent(item) [TalentID] " + IntToString(nInput) + " [Target] " + GetName(oInput) + " [Location] " + sInput; break;
case 8: sDebug = "[DCR:Casting] Workaround for Spontaeous [SpellID] " + IntToString(nInput) + " [Target] " + GetName(oInput); break;
case 9: sDebug = "[DCR:Casting] NormalSpell [ID] " + IntToString(nInput) + " [Target] " + GetName(oInput) + " [Location] " + sInput; break;
case 10: sDebug = "[DCR:Casting] TalentSpell. [ID] " + IntToString(nInput) + " [Target] " + GetName(oInput) + " [Location] " + sInput; break;
case 11: sDebug = "[DCR:Casting] SubSpecialSpell. [ID] " + IntToString(nInput) + " [Target] " + GetName(oInput) + " [Location] " + sInput; break;
case 12: sDebug = "[DCR:Casting] NormalRandomSpell. [ID] " + IntToString(nInput) + " [Target] " + GetName(oInput) + " [Location] " + sInput; break;
case 13: sDebug = "[DCR:Casting] Backup spell caught: " + IntToString(nInput); break;
case 14: sDebug = "[DCR:Feat] [ID] " + IntToString(nInput) + " [Enemy] " + GetName(oInput); break;
case 15: sDebug = "[DCR:Casting] Grenade [ID] " + IntToString(nInput) + " [Target] " + GetName(oInput) + " [Location] " + sInput; break;
case 16: sDebug = "[AOE Call] Moving out of/Dispeling an AOE. [Tag] " + GetTag(oInput); break;
case 17: sDebug = "[DCR:Special] Darkness + Caster. No seen enemy. Dispel/Move."; break;
case 18: sDebug = "[DRC:Talent] Using Talent (Healing). [TalentID] " + IntToString(iInput) + " [Target] " + GetName(oInput); break;
case 19: sDebug = "[DCR:Healing] (Should) Healing [Target]" + GetName(oInput) + " [CurrentHP|Max|ID|Rank|Power] " + IntToString(iInput); break;
case 18: sDebug = "[DRC:Talent] Using Talent (Healing). [TalentID] " + IntToString(nInput) + " [Target] " + GetName(oInput); break;
case 19: sDebug = "[DCR:Healing] (Should) Healing [Target]" + GetName(oInput) + " [CurrentHP|Max|ID|Rank|Power] " + IntToString(nInput); break;
case 20: sDebug = "[DCR Healing] Boss Action, create Critical Wounds potion"; break;
case 21: sDebug = "[DCR:Casting] Healing self with healing kit, [Kit] " + GetName(oInput); break;
case 22: sDebug = "[DCR:Feat] Summoning my familiar"; break;
@@ -86,18 +85,18 @@ void DebugActionSpeakByInt(int iInteger, object oInput = OBJECT_INVALID, int iIn
case 29: sDebug = "[DCR:Bard Song] Using"; break;
case 30: sDebug = "[DCR:Bard Curse Song] Using"; break;
case 31: sDebug = "[DCR:All Spells] Error! No casting (No spells, items, target Etc)."; break;
case 32: sDebug = "[DCR:All Spells] [Modifier|BaseDC|SRA] " + IntToString(iInput); break;
case 33: sDebug = "[DCR:Casting] Cheat Spell. End of Spells. [Spell] " + IntToString(iInput) + "[Target]" + GetName(oInput); break;
case 32: sDebug = "[DCR:All Spells] [Modifier|BaseDC|SRA] " + IntToString(nInput); break;
case 33: sDebug = "[DCR:Casting] Cheat Spell. End of Spells. [Spell] " + IntToString(nInput) + "[Target]" + GetName(oInput); break;
case 34: sDebug = "[DCR:All Spells] Ranged Spells. Should use closer spells/move nearer"; break;
case 35: sDebug = "[DCR:Dragon] Breath weapon & attacking [Breath ID] " + IntToString(iInput) + " [Target] " + GetName(oInput); break;
case 35: sDebug = "[DCR:Dragon] Breath weapon & attacking [Breath ID] " + IntToString(nInput) + " [Target] " + GetName(oInput); break;
case 36: sDebug = "[DCR:Dragon] Wing Buffet [Target] " + GetName(oInput); break;
case 37: sDebug = "[DCR:Beholder] Teleport"; break;
case 38: sDebug = "[DCR:Beholder] Rays"; break;
case 39: sDebug = "[DCR:Targeting] No valid enemies in sight, moving to allies target's. [Target] " + GetName(oInput); break;
case 40: sDebug = "[DCR:Targeting] Override Target Seen. [Name]" + GetName(oInput); break;
case 41: sDebug = "[DCR:Targeting] No seen in LOS, Attempting to MOVE to something [Target]" + GetName(oInput); break;
case 42: sDebug = "[DCR:Skill] Using agressive skill (+Attack). [Skill] " + IntToString(iInput) + " [Enemy]" + GetName(oInput); break;
case 43: sDebug = "[DCR:Pre-Melee Spells] All Potions Using. [Spell ID] " + IntToString(iInput); break;
case 42: sDebug = "[DCR:Skill] Using agressive skill (+Attack). [Skill] " + IntToString(nInput) + " [Enemy]" + GetName(oInput); break;
case 43: sDebug = "[DCR:Pre-Melee Spells] All Potions Using. [Spell ID] " + IntToString(nInput); break;
case 44: sDebug = "[DCR:Pre-Melee Spells] True Strike Emptive attack [Target] " + GetName(oInput); break;
case 45: sDebug = "[DCR:CounterSpell] Counterspelling. [Target] " + GetName(oInput); break;
case 46: sDebug = "[DRC] START [Intruder]" + GetName(oInput); break;
@@ -119,26 +118,26 @@ void DebugActionSpeakByInt(int iInteger, object oInput = OBJECT_INVALID, int iIn
case 59: sDebug = "[Phisically Attacked] Attacking back. [Attacker(enemy)] " + GetName(oInput); break;
case 60: sDebug = "[Phisically Attacked] Not same area. [Attacker(enemy)] " + GetName(oInput); break;
// Damaged
case 61: sDebug = "[Damaged] Morale Penalty for 600 seconds [Penalty]" + IntToString(iInput); break;
case 61: sDebug = "[Damaged] Morale Penalty for 600 seconds [Penalty]" + IntToString(nInput); break;
case 62: sDebug = "[Damaged] Not in combat: DCR [Damager]" + GetName(oInput); break;
case 63: sDebug = "[Damaged] Not in combat: DCR. Ally hit us. [Damager(Ally?)]" + GetName(oInput); break;
// Death
case 64: sDebug = "[Death] Checking corpse status in " + IntToString(iInput) + " [Killer] " + GetName(oInput) + " [Times Died Now] " + sInput; break;
case 64: sDebug = "[Death] Checking corpse status in " + IntToString(nInput) + " [Killer] " + GetName(oInput) + " [Times Died Now] " + sInput; break;
// Disturbed
case 65: sDebug = "[Disturbed] (pickpocket) Attacking Enemy [Disturber] " + GetName(oInput) + " [Type] " + IntToString(iInput); break;
case 65: sDebug = "[Disturbed] (pickpocket) Attacking Enemy [Disturber] " + GetName(oInput) + " [Type] " + IntToString(nInput); break;
// Rest
case 66: sDebug = "[Rested] Resting. [Type(should be invalid)] " + IntToString(iInput); break;
case 66: sDebug = "[Rested] Resting. [Type(should be invalid)] " + IntToString(nInput); break;
// Spell Cast at
case 67: sDebug = "[Spell] Caster isn't a creature! May look for target [Caster] " + GetName(oInput); break;
case 68: sDebug = "[Spell:Enemy/Hostile] Not in combat. Attacking: [Caster] " + GetName(oInput); break;
case 69: sDebug = "[Spell] (ally). Not in combat. May Attack/Move [Caster] " + GetName(oInput); break;
// Spell Other AI
// - Shouts
case 70: sDebug = "[Shout] Reacting To Shout. [ShoutNo.] " + IntToString(iInput) + " [Shouter] " + GetName(oInput); break;
case 70: sDebug = "[Shout] Reacting To Shout. [ShoutNo.] " + IntToString(nInput) + " [Shouter] " + GetName(oInput); break;
// Constants
// - Search
case 71: sDebug = "[Search] Resting"; break;
case 72: sDebug = "[Search] Searching, No one to attack. [Time] " + sInput; break;
case 72: sDebug = "[Search] Searching, No one to attack. [Rounds Remaining] " + IntToString(nInput) + ". [Possible target] " + GetName(oInput); break;
// - DCR
case 73: sDebug = "[Call for DCR] Default AI [Pre-Set Target]" + GetName(oInput); break;
case 74: sDebug = "[Call for DCR] Custom AI [" + sInput + "] [Pre-Set Target]" + GetName(oInput); break;
@@ -160,14 +159,14 @@ void DebugActionSpeakByInt(int iInteger, object oInput = OBJECT_INVALID, int iIn
void DebugActionSpeak(string sString)
{
// You MUST uncomment this line, IF you use either of the below things
//string sNew = "[Debug]" + GetName(OBJECT_SELF) + "[ObjectID]" + ObjectToString(OBJECT_SELF) + " [Debug] " + sString;
string sNew = "[Debug]" + GetName(OBJECT_SELF) + "[ObjectID]" + ObjectToString(OBJECT_SELF) + " [Debug] " + sString;
// Note, uncomment this, so that DM's can hear the debug speaks, normally it is
// only server admins who can hear the debug. If you are not testing, it might
// be best to keep this uncommented.
// Futher: - Must have debug mode set to 1
// - Only the server admin can seem to see this.
//SpeakString(sNew, TALKVOLUME_SILENT_TALK);
// SpeakString(sNew, TALKVOLUME_TALK);
// Note, uncomment this line to send a message to the first PC in the module.
// - Useful for singleplayer testing
@@ -176,13 +175,13 @@ void DebugActionSpeak(string sString)
// This writes the entry to the log, very important, if debugging
// Futher: - If left up for a long time, logs can get very big with the AI
// - Use to find problems in the AI and report to me :-D (Jasperre)
//WriteTimestampedLogEntry(sNew);
WriteTimestampedLogEntry(sNew);
}
// Debug: To compile this script full, uncomment all of the below.
/*
/* - Add two "/"'s at the start of this line
void main()
{
DebugActionSpeak("Test");
return;
}
*/
//*/

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +1,29 @@
/************************ [Heartbeat Include] **********************************
Filename: J_Inc_Heartbeat
************************* [Heartbeat Include] **********************************
/*/////////////////////// [Include - Heartbeat] ////////////////////////////////
Filename: J_INC_Heartbeat
///////////////////////// [Include - Heartbeat] ////////////////////////////////
This contains any heartbeat function calls.
Note that the heartbeat uses ExecuteScript for larget behaviours that are
better split up so the heartbeat is as tiny as possible.
************************* [History] ********************************************
1.3 After Beta - Added
************************* [Workings] *******************************************
This is included in nw_c2_default1 and j_ai_onheartbeat.
///////////////////////// [History] ////////////////////////////////////////////
1.3 - After Beta - Added
1.4 - TO DO
- Add in some function (see rest script) that resets if we are not in
combat
- Some more of the things we should do even if interrupted not the
heartbeat.
Contains things like in j_inc_other_ai
************************* [Arguments] ******************************************
- Have moved "after combat searching" into here. It isn't long - but
it is more reliable. The special action is cancled if there is combat
going on, of course.
///////////////////////// [Workings] ///////////////////////////////////////////
This is included in nw_c2_default1 and J_AI_OnHeartbeat.
Contains things like in J_INC_OTHER_AI, but only for the heartbeat event.
Keeps it cleaner to read.
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: N/A
************************* [Heartbeat Include] *********************************/
///////////////////////// [Include - Heartbeat] //////////////////////////////*/
#include "J_INC_CONSTANTS"
@@ -23,8 +33,6 @@ const string sWalkwayVarname = "NW_WALK_CONDITION";
const int NW_WALK_FLAG_CONSTANT = 0x00000002;
// Checks:
// * Dead
// * Uncommandable
// * No valid location
// * Petrified, paralised, ETC.
// Note: If sleep is found, it may apply Zzzz randomly, as well as stopping.
@@ -42,68 +50,82 @@ int GetWalkCondition(int nCondition, object oCreature=OBJECT_SELF);
// - Invisiblity (best)
// - Haste/Expeditious Retreat
void ActionCastFleeingSpells();
// Cast fleeing spells.
// - Invisiblity (best)
// - Haste/Expeditious Retreat
void ActionCastMoveToCombatSpells();
// Attempt to cast iSpell. TRUE if true.
int FleeingSpellCast(int iSpell);
// Attempt to cast nSpell. TRUE if true.
// Searching and fleeing spells use this.
int HeartbeatSpellCast(int nSpell);
// Used in Search(). This apply Trueseeing, See invisibility, or Invisiblity purge
// if we have neither of the 3 on us.
void SearchSpells();
// Returns TRUE if any of the animation settings are on.
int GetHasValidAnimations();
// Checks:
// * No valid location
// * Petrified, paralised, ETC.
// Note: If sleep is found, it may apply Zzzz randomly, as well as stopping.
int JumpOutOfHeartBeat()
{
// What to return
int iReturn = FALSE;
int bReturn = FALSE;
// Checks:
// * Dead + Uncommandable are in GetAIOff
// * No valid location
// * Petrified, paralised, ETC.
// Note: If sleep is found, it may apply Zzzz randomly, as well as stopping.
// Effect checking
effect eCheck = GetFirstEffect(OBJECT_SELF);
int iEffectType;
while(GetIsEffectValid(eCheck) && iReturn == FALSE)
int nEffectType;
while(GetIsEffectValid(eCheck) && bReturn == FALSE)
{
iEffectType = GetEffectType(eCheck);
nEffectType = GetEffectType(eCheck);
// Sleep is special
if(iEffectType == EFFECT_TYPE_SLEEP)
if(nEffectType == EFFECT_TYPE_SLEEP)
{
iReturn = i2;// This immediantly breaks.
bReturn = 2;// This immediantly breaks.
}
// ALL these stop heartbeat.
else if(iEffectType == EFFECT_TYPE_PARALYZE || iEffectType == EFFECT_TYPE_STUNNED ||
iEffectType == EFFECT_TYPE_FRIGHTENED || /* Removed sleep above */
iEffectType == EFFECT_TYPE_TURNED || iEffectType == EFFECT_TYPE_PETRIFY ||
iEffectType == EFFECT_TYPE_DAZED || iEffectType == EFFECT_TYPE_TIMESTOP ||
iEffectType == EFFECT_TYPE_DISAPPEARAPPEAR || iEffectType == EFFECT_TYPE_CHARMED ||
iEffectType == EFFECT_TYPE_DOMINATED || iEffectType == EFFECT_TYPE_CONFUSED)
else if(nEffectType == EFFECT_TYPE_PARALYZE || nEffectType == EFFECT_TYPE_STUNNED ||
nEffectType == EFFECT_TYPE_FRIGHTENED || /* Removed sleep above */
nEffectType == EFFECT_TYPE_TURNED || nEffectType == EFFECT_TYPE_PETRIFY ||
nEffectType == EFFECT_TYPE_DAZED || nEffectType == EFFECT_TYPE_TIMESTOP ||
nEffectType == EFFECT_TYPE_DISAPPEARAPPEAR || nEffectType == EFFECT_TYPE_CHARMED ||
nEffectType == EFFECT_TYPE_DOMINATED || nEffectType == EFFECT_TYPE_CONFUSED)
{
iReturn = i1;// 1 = No Zzz. We continue to check for Zzz as well.
bReturn = 1;// 1 = No Zzz. We continue to check for Zzz as well.
}
eCheck = GetNextEffect(OBJECT_SELF);
}
// Do we fire the heartbeat event?
if(iReturn != FALSE)
if(bReturn != FALSE)
{
// If it is sleep... Zzzzz sometimes.
if(iReturn == i2 && d6() == i1)
if(bReturn == 2 && d6() == 1)
{
ApplyEffectToObject(DURATION_TYPE_INSTANT,
EffectVisualEffect(VFX_IMP_SLEEP),
OBJECT_SELF);
}
FireUserEvent(
AI_FLAG_UDE_HEARTBEAT_EVENT,
EVENT_HEARTBEAT_EVENT);// Fire event 1001
// Fire event 1001
FireUserEvent(AI_FLAG_UDE_HEARTBEAT_EVENT, EVENT_HEARTBEAT_EVENT);
}
return iReturn;
return bReturn;
}
// This checks fleeing, door bashing and so on, to stop the heartbeat
// and perform the override special action, rather then run normal behaviour.
int PerformSpecialAction()
{
int iAction = GetCurrentSetAction();
int nAction = GetCurrentSetAction();
object oTarget = GetAttackTarget();
object oRunTarget;
switch(iAction)
switch(nAction)
{
// - Leader has made me a runner. I must run to a nearby group calling
// for help to get more men
@@ -116,7 +138,7 @@ int PerformSpecialAction()
{
// Stop thinking we are a runner if we can see the run target
ResetCurrentAction();
AISpeakString(HELP_MY_FRIEND);
AISpeakString(AI_SHOUT_HELP_MY_FRIEND);
return FALSE;
}
else
@@ -124,7 +146,7 @@ int PerformSpecialAction()
// Else run to them
if(GetObjectHeard(oRunTarget))
{
AISpeakString(HELP_MY_FRIEND);
AISpeakString(AI_SHOUT_HELP_MY_FRIEND);
}
ClearAllActions();
ActionMoveToObject(oRunTarget, TRUE);
@@ -149,26 +171,34 @@ int PerformSpecialAction()
// at higher intelligence) things like Expeditious retreat.
// - Only used once - one invisibility or haste. Deleted above.
ActionCastFleeingSpells();
ActionForceFollowObject(oRunTarget, f3);
ActionForceFollowObject(oRunTarget, 3.0);
}
else if(GetObjectSeen(oRunTarget))
{
// If we see the flee target, reset targets
ResetCurrentAction();
// We will delete the local int (set to TRUE) which we
// stopped fleeing spells from
// stopped fleeing spells from being used
DeleteAIInteger(AI_HEARTBEAT_FLEE_SPELLS);
// Speak to allies to come :-)
AISpeakString(HELP_MY_FRIEND);
// Return FALSE.
return FALSE;
AISpeakString(AI_SHOUT_HELP_MY_FRIEND);
// Also reset visual effect
RemoveFleeingVisual();
// And attack/heal self
ClearAllActions();
DetermineCombatRound();
// Return TRUE, we attacked
return TRUE;
}
else
{
// Else flee!
if(GetObjectHeard(oRunTarget))
{
AISpeakString(HELP_MY_FRIEND);
AISpeakString(AI_SHOUT_HELP_MY_FRIEND);
}
ClearAllActions();
// New - cast fleeing spells. Important (and only used
@@ -181,34 +211,53 @@ int PerformSpecialAction()
}
else
{
// Check if we have bad intellgence, and we will run away
// from the nearest enemy if heard.
if(GetAIInteger(AI_INTELLIGENCE) <= i3)
// Check if we have bad intellgence, if we have, we will run away
// from the nearest enemy we can see or hear.
if(GetAIInteger(AI_INTELLIGENCE) <= 3)
{
oRunTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE);
oRunTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE);
if(!GetIsObjectValid(oRunTarget))
{
oRunTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD, CREATURE_TYPE_IS_ALIVE, TRUE);
oRunTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD, CREATURE_TYPE_IS_ALIVE, TRUE);
if(!GetIsObjectValid(oRunTarget))
{
oRunTarget = GetLastHostileActor();
if(!GetIsObjectValid(oRunTarget) || GetIsDead(oRunTarget))
{
// If we do not have anyone to run from, stop
ResetCurrentAction();
return FALSE;
// Speak to allies to come :-)
AISpeakString(AI_SHOUT_HELP_MY_FRIEND);
// Also reset visual effect
RemoveFleeingVisual();
// And attack/heal self
ClearAllActions();
DetermineCombatRound();
// Return TRUE, we attacked
return TRUE;
}
}
}
// Run from enemy
// Run from enemy (1.4: Was oTarget, now oRunTarget)
ClearAllActions();
ActionMoveAwayFromObject(oTarget, TRUE, f50);
ActionMoveAwayFromObject(oRunTarget, TRUE, 50.0);
return TRUE;
}
// If we see the flee target, reset targets
ResetCurrentAction();
return FALSE;
// Speak to allies to come :-)
AISpeakString(AI_SHOUT_HELP_MY_FRIEND);
// Also reset visual effect
RemoveFleeingVisual();
// And attack/heal self
ClearAllActions();
DetermineCombatRound();
// Return TRUE, we attacked/healed
return TRUE;
}
}
break;
// If this is set, we are usually in combat - and must move out of an AOE.
case AI_SPECIAL_ACTIONS_MOVE_OUT_OF_AOE:
{
// We must be X distance away from a cirtain AOE, if we are not, we
@@ -233,6 +282,203 @@ int PerformSpecialAction()
}
}
break;
// If this is the one, we will search around for enemies - usually done
// at the end of a combat round, it is more reliable here.
case AI_SPECIAL_ACTIONS_SEARCH_AROUND:
{
// If we are in combat, delete this special thing, and return FALSE
if(GetIsObjectValid(GetAttemptedSpellTarget()) ||
GetIsObjectValid(GetAttemptedAttackTarget()) ||
GetIsObjectValid(GetAttackTarget()))
{
// Reset, and return FALSE.
ResetCurrentAction();
return FALSE;
}
// Added this so special actions do not get ignored (EG: healkitting)
// It will not do anything, but no heartbeat will be performed. These
// kind of actions happen at the end of combat (healing self of damage ETC)
// So, basically, will keep in mind it's still searching, but will leave
// it until no busy actions are being done.
else if(GetIsBusyWithAction())
{
return TRUE;
}
// We search for a cirtain number of rounds, set in the generic AI
// file, when we first start searching, or restart even. The generic
// AI will not actually do search actions, and if it finds no enemy,
// will probably just increase the integer to do more search rounds.
// * Will be intelligence + 2 to start.
int nRoundsRemaining = GetAIInteger(AI_SEARCH_ROUNDS_REMAINING);
// Decrease rounds remaining
nRoundsRemaining--;
// Set new one onto us to use next time
SetAIInteger(AI_SEARCH_ROUNDS_REMAINING, nRoundsRemaining);
// * Note: If nRoundsRemaining is 0 at the end of this function, we
// will remove this action as the current special one.
// Get the target to move to/around
// * Can be invalid, but usually the creature we just killed or noticed
// lying on the ground.
object oTarget = GetAIObject(AI_SEARCH_TARGET);
// Stop now (Small amounts of movement each time seem more cautious)
ClearAllActions();
// Check some spells. Cast one if we have no true seeing ETC.
SearchSpells();
// Stealth/search.
int bStealth = GetStealthMode(OBJECT_SELF);
int bSearch = GetDetectMode(OBJECT_SELF);
// We perfere to hide again if we search if set to...sneaky!
if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_HIDING, AI_OTHER_COMBAT_MASTER))
{
if(bStealth != STEALTH_MODE_ACTIVATED)
{
SetActionMode(OBJECT_SELF, ACTION_MODE_STEALTH, TRUE);
}
}
else
{
// If we are hiding, stop to search (we shouldn't be - who knows?)
if(bStealth == STEALTH_MODE_ACTIVATED)
{
SetActionMode(OBJECT_SELF, ACTION_MODE_STEALTH, FALSE);
}
// And search!
if(bSearch != DETECT_MODE_ACTIVE && !GetHasFeat(FEAT_KEEN_SENSE))
{
SetActionMode(OBJECT_SELF, ACTION_MODE_DETECT, TRUE);
}
}
// We check around the target, if there is one.
if(GetIsObjectValid(oTarget))
{
// Move to the location of oTarget
ActionMoveToLocation(GetLocation(oTarget));
// If it is a chest ETC. We close it.
if(GetIsOpen(oTarget))
{
if(GetObjectType(oTarget) == OBJECT_TYPE_DOOR)
{
ActionCloseDoor(oTarget);
}
else
{
// Close it
ActionDoCommand(DoPlaceableObjectAction(oTarget, PLACEABLE_ACTION_USE));
}
}
}
// We will get nearest enemy at the very least
else
{
// Use nearest heard
object oEnemy = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY,
OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD);
if(GetIsObjectValid(oEnemy))
{
// Move to location
ActionMoveToLocation(GetLocation(oEnemy));
}
}
// Note: Here, we will return to spawn location after moving to the
// object, if it is a valid setting, else we do the normal randomwalk
if(GetSpawnInCondition(AI_FLAG_OTHER_RETURN_TO_SPAWN_LOCATION, AI_OTHER_MASTER))
{
ActionMoveToLocation(GetAILocation(AI_RETURN_TO_POINT));
}
else
{
// 72: "[Search] Searching, No one to attack. [Rounds Remaining] " + IntToString(nRoundsRemaining) + ". [Possible target] " + GetName(oTarget)
DebugActionSpeakByInt(72, oTarget, nRoundsRemaining);
// Randomly walk.
ActionRandomWalk();
}
// If we have 0 rounds left of searching time, we turn of this special
// action, walk waypoints, and probably rest.
if(nRoundsRemaining == 0)
{
// Rest after combat?
if(GetSpawnInCondition(AI_FLAG_OTHER_REST_AFTER_COMBAT, AI_OTHER_MASTER))
{
// 71: "[Search] Resting"
DebugActionSpeakByInt(71);
// Yes - we use ActionRest(). It can possibly still fail if
// enemies are still around!
ActionRest();
ActionWait(1.0);
}
else
{
// Else, just execute Walk Waypoints
ExecuteScript(FILE_WALK_WAYPOINTS, OBJECT_SELF);
}
// Delete this special action
ResetCurrentAction();
}
// If we havn't bailed out early and returned FALSE (do normal hb) we
// will return TRUE, we have something to do at least.
return TRUE;
}
break;
// Move to combat - we buff (and only buff again after 1 minute of running)
// and either follow the person who wants us to help them, or we will run to the
// location set.
// * Set only by "AI_HELP_MY_FRIEND_CONSTANT" at the moment.
case AI_SPECIAL_ACTIONS_MOVE_TO_COMBAT:
{
// We get a location to move to first
location lTarget = GetAILocation(AI_MOVE_TO_COMBAT_LOCATION);
object oObject = GetAreaFromLocation(lTarget);
// Check if the location is valid
if(GetIsObjectValid(oObject) && GetArea(OBJECT_SELF) == oObject)
{
// Just move, rapidly, to lTarget.
ClearAllActions();
// Buff up an action
ActionCastMoveToCombatSpells();
// Move (fast) to that location
ActionMoveToLocation(lTarget, TRUE);
// If we see/hear combat, we'll attack
return TRUE;
}
else
{
// Get who we should "follow" or move to.
oObject = GetAIObject(AI_MOVE_TO_COMBAT_OBJECT);
if(GetIsObjectValid(oObject))
{
// Just move, rapidly, to oTarget. It isn't "real" following,
// but paced. Means it looks OK. Need to test - but should be OK.
ClearAllActions();
// Buff up an action
ActionCastMoveToCombatSpells();
// Move (fast) to that location
ActionMoveToObject(oTarget, TRUE);
// If we see/hear combat, we'll attack
return TRUE;
}
else
{
// Remove the special action and return FALSE
ResetCurrentAction();
return FALSE;
}
}
}
break;
}
// Return false to carry on a normal heartbeat
return FALSE;
@@ -256,21 +502,103 @@ void ActionCastFleeingSpells()
SetAIInteger(AI_HEARTBEAT_FLEE_SPELLS, TRUE);
// Invisibilities
if(FleeingSpellCast(SPELL_IMPROVED_INVISIBILITY)) return;
if(FleeingSpellCast(SPELL_INVISIBILITY)) return;
if(HeartbeatSpellCast(SPELL_IMPROVED_INVISIBILITY)) return;
if(HeartbeatSpellCast(SPELL_INVISIBILITY)) return;
// Haste
if(FleeingSpellCast(SPELL_MASS_HASTE)) return;
if(FleeingSpellCast(SPELL_HASTE)) return;
if(FleeingSpellCast(SPELL_EXPEDITIOUS_RETREAT)) return;
if(HeartbeatSpellCast(SPELL_MASS_HASTE)) return;
if(HeartbeatSpellCast(SPELL_HASTE)) return;
if(HeartbeatSpellCast(SPELL_EXPEDITIOUS_RETREAT)) return;
}
// Attempt to cast iSpell. TRUE if true.
int FleeingSpellCast(int iSpell)
// Cast fleeing spells.
// - Invisiblity (best)
// - Haste/Expeditious Retreat
void ActionCastMoveToCombatSpells()
{
if(GetHasSpell(iSpell))
// Timer to stop too many spells at once
if(GetLocalTimer(AI_TIMER_MOVE_TO_COMBAT_BUFF)) return;
// We first will cast a preperation spell before jumping in!
// This is used once per minute.
SetLocalTimer(AI_TIMER_MOVE_TO_COMBAT_BUFF, 60.0);
// We possibly cast a few spell first - stoneskin range, see
// invisible range, and invisibility range.
// Protection things
// * Cast 1 spell!
if(HeartbeatSpellCast(SPELL_PREMONITION)) return;
if(HeartbeatSpellCast(SPELL_GREATER_STONESKIN)) return;
if(HeartbeatSpellCast(SPELL_STONESKIN)) return;
// Invisibility range
if(HeartbeatSpellCast(SPELL_ETHEREALNESS)) return;
if(HeartbeatSpellCast(SPELL_IMPROVED_INVISIBILITY)) return;
if(HeartbeatSpellCast(SPELL_INVISIBILITY_SPHERE)) return;
if(HeartbeatSpellCast(SPELL_INVISIBILITY)) return;
// See invisible things
if(HeartbeatSpellCast(SPELL_TRUE_SEEING)) return;
if(HeartbeatSpellCast(SPELL_SEE_INVISIBILITY)) return;
// Stealth! Only if we are good at it, of course.
// Spawn in conditions for it
if(!GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_HIDING, AI_OTHER_COMBAT_MASTER))
{
// Need skill or force on
if((GetSkillRank(SKILL_HIDE) - 4 >= GetHitDice(OBJECT_SELF)) ||
GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_HIDING, AI_OTHER_COMBAT_MASTER))
{
// Use hide
SetActionMode(OBJECT_SELF, ACTION_MODE_STEALTH, TRUE);
return;
}
}
}
// Used in Search(). This apply Trueseeing, See invisibility, or Invisiblity purge
// if we have neither of the 3 on us.
void SearchSpells()
{
effect eCheck = GetFirstEffect(OBJECT_SELF);
int nEffectType, bBreak;
while(GetIsEffectValid(eCheck) && bBreak == FALSE)
{
nEffectType = GetEffectType(eCheck);
if(nEffectType == EFFECT_TYPE_TRUESEEING ||
nEffectType == EFFECT_TYPE_SEEINVISIBLE)
{
bBreak = TRUE;
}
eCheck = GetNextEffect(OBJECT_SELF);
}
// We have effects, stop.
if(bBreak == TRUE || GetHasSpellEffect(SPELL_INVISIBILITY_PURGE))
{
return;
}
// Else we apply the best spell we have.
if(HeartbeatSpellCast(SPELL_TRUE_SEEING)) return;
if(HeartbeatSpellCast(SPELL_SEE_INVISIBILITY)) return;
if(HeartbeatSpellCast(SPELL_INVISIBILITY_PURGE)) return;
}
// Attempt to cast nSpell. TRUE if true.
int HeartbeatSpellCast(int nSpell)
{
// 1.4: added check to see if has effect already
if(GetHasSpell(nSpell) && !GetHasSpellEffect(nSpell, OBJECT_SELF))
{
ActionCastSpellAtObject(nSpell, OBJECT_SELF);
return TRUE;
}
return FALSE;
}
// Returns TRUE if any of the animation settings are on.
int GetHasValidAnimations()
{
int nCheck = GetLocalInt(OBJECT_SELF, NW_GENERIC_MASTER);
if((nCheck & NW_FLAG_AMBIENT_ANIMATIONS) ||
(nCheck & NW_FLAG_AMBIENT_ANIMATIONS_AVIAN) ||
(nCheck & NW_FLAG_IMMOBILE_AMBIENT_ANIMATIONS))
{
ActionCastSpellAtObject(iSpell, OBJECT_SELF);
return TRUE;
}
return FALSE;

View File

@@ -1,6 +1,6 @@
/************************ [Combat Attack] **************************************
Filename: j_inc_npc_attack
************************* [Combat Attack] **************************************
/*/////////////////////// [Include - NPC (Combat) Attack] //////////////////////
Filename: J_INC_NPC_Attack
///////////////////////// [Include - NPC (Combat) Attack] //////////////////////
What does this do?
It is a wrapper/include for getting a creature to attack target X, or do
@@ -11,17 +11,20 @@
And it also keeps Combat files SMALL! I uses Execute Script to fire the
combat file, not include it here.
************************* [History] ********************************************
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Added
************************* [Workings] *******************************************
1.4 - TO DO:
- Bugfix a few things (copy/paste errors)
- Add example script to use (User defined events)
///////////////////////// [Workings] ///////////////////////////////////////////
Include this in any conversation file or whatever, and mearly read the
descriptions of the different functions, and it will do what it says :-)
************************* [Arguments] ******************************************
Arguments:
************************* [Combat Attack] *************************************/
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: N/A
///////////////////////// [Include - NPC (Combat) Attack] ////////////////////*/
// Include the constants for the combat, spawn integers ETC.
#include "j_inc_constants"
#include "J_INC_CONSTANTS"
// Hostile amount
const int HOSTILE = -100;// Reputation to change to
@@ -41,22 +44,22 @@ void DetermineSpeakCombatRoundNotMe(object oTarget, object oAttacker);
// This is the main wrapper to get an NPC to attack in conversation.
// * fDelay - The delay AFTER adjusting reputation, that we attack and shout
// * iPlot - The plot flag to set to (Usually FALSE).
// * iImmortal - The immortal flag to set to (Usually FALSE).
// * bPlot - The plot flag to set US to (Usually FALSE).
// * bImmortal - The immortal flag US to set to (Usually FALSE).
// Example, how to keep flags already set:
// HostileAttackPCSpeaker(0.0, GetPlotFlag(), GetImmortal());
// * iAllAllies - This will determine combat rounds against the target, that are in 50.0M. False = Don't
void HostileAttackPCSpeaker(float fDelay = 0.0, int iPlot = FALSE, int iImmortal = FALSE, int iAllAllies = TRUE);
// * bAllAllies - This will determine combat rounds against the target, that are in 50.0M. False = Don't
void HostileAttackPCSpeaker(float fDelay = 0.0, int bPlot = FALSE, int bImmortal = FALSE, int bAllAllies = TRUE);
// This will make our faction hostile to the target, and attack them.
// * oTarget - The target object to attack
// * fDelay - The delay AFTER adjusting reputation, that we attack and shout
// * iPlot - The plot flag to set to (Usually FALSE).
// * iImmortal - The immortal flag to set to (Usually FALSE).
// * bPlot - The plot flag to set US to (Usually FALSE).
// * bImmortal - The immortal flag US to set to (Usually FALSE).
// Example, how to keep flags already set:
// HostileAttackObject(oPC, 0.0, GetPlotFlag(), GetImmortal());
// * iAllAllies - This will determine combat rounds against the target, that are in 50.0M. False = Don't
void HostileAttackObject(object oTarget, float fDelay = 0.0, int iPlot = FALSE, int iImmortal = FALSE, int iAllAllies = TRUE);
// * bAllAllies - This will determine combat rounds against the target, that are in 50.0M. False = Don't
void HostileAttackObject(object oTarget, float fDelay = 0.0, int bPlot = FALSE, int bImmortal = FALSE, int bAllAllies = TRUE);
// This will make our faction hostile to the target, and shout.
// * oTarget - The target object to shout about.
@@ -65,25 +68,25 @@ void HostileAttackObject(object oTarget, float fDelay = 0.0, int iPlot = FALSE,
void ShoutAbout(object oTarget);
// This will make our faction hostile to ALL(!) PC's...in the area or game or range
// * iType - TYPE_ALL_PCS (1) is all PC's in the world.
// * nType - TYPE_ALL_PCS (1) is all PC's in the world.
// - TYPE_ALL_AREA (2) is all PC's in the specific area.
// - TYPE_IN_RANGE (3) is all PC's within fRange.
// * iPlot - The plot flag to set to (Usually FALSE).
// * iImmortal - The immortal flag to set to (Usually FALSE).
// * iAllAllies - This will determine combat rounds against the target, that are in 50.0M. False = Don't
void HostileAttackAllPCs(int iType = 1, float fRange = 40.0, int iPlot = FALSE, int iImmortal = FALSE, int iAllAllies = TRUE);
// * bPlot - The plot flag to set US to (Usually FALSE).
// * bImmortal - The immortal flag US to set to (Usually FALSE).
// * bAllAllies - This will determine combat rounds against the target, that are in 50.0M. False = Don't
void HostileAttackAllPCs(int nType = 1, float fRange = 40.0, int bPlot = FALSE, int bImmortal = FALSE, int bAllAllies = TRUE);
// This will thier most damaging weapon, and wait to disarm it.
// * fDuration - Delay until the weapon is withdrawn.
// * iRanged - if TRUE, it will equip a ranged weapon as a prioritory (EquipRanged call)
void EquipWeaponsDuration(float fDuration, int iRanged = FALSE);
// * bRanged - if TRUE, it will equip a ranged weapon as a prioritory (EquipRanged call)
void EquipWeaponsDuration(float fDuration, int bRanged = FALSE);
// Disarms the persons right-hand-weapon
void RemoveWeapons();
// Plays talks like "ATTACK!" and "Group Near Me" etc.
// * iLowest, iHighest - the High/Lowest value to use.
// * nLowest, nHighest - the High/Lowest value to use.
// 0 = ATTACK, 1 = TAUNT, 2-4 = BATTLE(1-3), 5 = ENEMIES, 6 = GROUP, 7 = HELP.
void PlaySomeTaunt(int iLowest = 0, int iHighest = 7);
void PlaySomeTaunt(int nLowest = 0, int nHighest = 7);
// Gets all allies of ourselves to attack oTarget
// * oTarget - The target to attack.
@@ -156,72 +159,75 @@ void DetermineSpeakCombatRoundNotMe(object oTarget, object oAttacker)
}
}
// This is the main wrapper to get an NPC to attack in conversation.
// * iPlot - The plot flag to set to (Usually FALSE).
// * iImmortal - The immortal flag to set to (Usually FALSE).
// * fDelay - The delay AFTER adjusting reputation, that we attack and shout
// * bPlot - The plot flag to set US to (Usually FALSE).
// * bImmortal - The immortal flag US to set to (Usually FALSE).
// Example, how to keep flags already set:
// AttackPCSpeaker(GetPlotFlag(), GetImmortal());
// * iAllAllies - This will determine combat rounds against the target, that are in 50.0M. False = Don't
void HostileAttackPCSpeaker(float fDelay = 0.0, int iPlot = FALSE, int iImmortal = FALSE, int iAllAllies = TRUE)
// HostileAttackPCSpeaker(0.0, GetPlotFlag(), GetImmortal());
// * bAllAllies - This will determine combat rounds against the target, that are in 50.0M. False = Don't
void HostileAttackPCSpeaker(float fDelay = 0.0, int bPlot = FALSE, int bImmortal = FALSE, int bAllAllies = TRUE)
{
// Get the PC
object oPC = GetPCSpeaker();
// Error checking
if(!GetIsObjectValid(oPC) || GetIsDM(oPC)) return;
// Change the flags
if(GetPlotFlag() != iPlot)
SetPlotFlag(OBJECT_SELF, iPlot);
if(GetImmortal() != iImmortal)
SetImmortal(OBJECT_SELF, iPlot);
// Change our flags for plot and immortal (usually turns them off)
SetPlotFlag(OBJECT_SELF, bPlot);
SetImmortal(OBJECT_SELF, bImmortal);
// We make them hostile to our faction
AdjustReputation(oPC, OBJECT_SELF, HOSTILE);
// Attack them
SetLocalObject(OBJECT_SELF, AI_TO_ATTACK, oPC);
if(fDelay > 0.0)
{
// Round start...
DelayCommand(fDelay, DetermineSpeakCombatRound(oPC, I_WAS_ATTACKED));
if(iAllAllies)
DelayCommand(fDelay, AlliesAttack(oPC));
DelayCommand(fDelay, DetermineSpeakCombatRound(oPC, AI_SHOUT_I_WAS_ATTACKED));
if(bAllAllies) DelayCommand(fDelay, AlliesAttack(oPC));
}
else
{
// Round start...
DetermineSpeakCombatRound(oPC, I_WAS_ATTACKED);
if(iAllAllies) AlliesAttack(oPC);
DetermineSpeakCombatRound(oPC, AI_SHOUT_I_WAS_ATTACKED);
if(bAllAllies) AlliesAttack(oPC);
}
}
// This will make our faction hostile to the target, and attack them.
// * oTarget - The target object to attack
// * iPlot - The plot flag to set to (Usually FALSE).
// * iImmortal - The immortal flag to set to (Usually FALSE).
// * fDelay - The delay AFTER adjusting reputation, that we attack and shout
// * bPlot - The plot flag to set US to (Usually FALSE).
// * bImmortal - The immortal flag US to set to (Usually FALSE).
// Example, how to keep flags already set:
// AttackObject(oPC, GetPlotFlag(), GetImmortal());
// * iAllAllies - This will determine combat rounds against the target, that are in 50.0M. False = Don't
void HostileAttackObject(object oTarget, float fDelay = 0.0, int iPlot = FALSE, int iImmortal = FALSE, int iAllAllies = TRUE)
// HostileAttackObject(oPC, 0.0, GetPlotFlag(), GetImmortal());
// * bAllAllies - This will determine combat rounds against the target, that are in 50.0M. False = Don't
void HostileAttackObject(object oTarget, float fDelay = 0.0, int bPlot = FALSE, int bImmortal = FALSE, int bAllAllies = TRUE)
{
// Error checking
if(!GetIsObjectValid(oTarget) || GetIsDM(oTarget)) return;
// Change the flags
if(GetPlotFlag() != iPlot)
SetPlotFlag(OBJECT_SELF, iPlot);
if(GetImmortal() != iImmortal)
SetImmortal(OBJECT_SELF, iPlot);
// Change our flags for plot and immortal (usually turns them off)
SetPlotFlag(OBJECT_SELF, bPlot);
SetImmortal(OBJECT_SELF, bImmortal);
// We make them hostile to our faction
AdjustReputation(oTarget, OBJECT_SELF, HOSTILE);
// Attack them
SetLocalObject(OBJECT_SELF, AI_TO_ATTACK, oTarget);
if(fDelay > 0.0)
{
// Round start...
DelayCommand(fDelay, DetermineSpeakCombatRound(oTarget, I_WAS_ATTACKED));
DelayCommand(fDelay, DetermineSpeakCombatRound(oTarget, AI_SHOUT_I_WAS_ATTACKED));
}
else
{
// Round start...
DetermineSpeakCombatRound(oTarget, I_WAS_ATTACKED);
DetermineSpeakCombatRound(oTarget, AI_SHOUT_I_WAS_ATTACKED);
}
}
@@ -233,23 +239,23 @@ void ShoutAbout(object oTarget)
// We make them hostile to our faction
AdjustReputation(oTarget, OBJECT_SELF, HOSTILE);
// And shout for others to attack
AISpeakString(CALL_TO_ARMS);
AISpeakString(AI_SHOUT_CALL_TO_ARMS);
}
// This will make our faction hostile to ALL(!) PC's...in the area or game or range
// * iType - TYPE_ALL_PCS (1) is all PC's in the world.
// * nType - TYPE_ALL_PCS (1) is all PC's in the world.
// - TYPE_ALL_AREA (2) is all PC's in the specific area.
// - TYPE_IN_RANGE (3) is all PC's within fRange.
// * iPlot - The plot flag to set to (Usually FALSE).
// * iImmortal - The immortal flag to set to (Usually FALSE).
// * iAllAllies - This will determine combat rounds against the target, that are in 50.0M. False = Don't
void HostileAttackAllPCs(int iType = 1, float fRange = 40.0, int iPlot = FALSE, int iImmortal = FALSE, int iAllAllies = TRUE)
// * bPlot - The plot flag to set US to (Usually FALSE).
// * bImmortal - The immortal flag US to set to (Usually FALSE).
// * bAllAllies - This will determine combat rounds against the target, that are in 50.0M. False = Don't
void HostileAttackAllPCs(int nType = 1, float fRange = 40.0, int bPlot = FALSE, int bImmortal = FALSE, int bAllAllies = TRUE)
{
object oPC, oToAttack;
int iShout, iCnt;
int bShout, nCnt;
float fNearestEnemy = 10000.0;
object oArea = GetArea(OBJECT_SELF);
switch(iType)
switch(nType)
{
case TYPE_ALL_PCS:// s all PC's in the world.
{
@@ -267,7 +273,7 @@ void HostileAttackAllPCs(int iType = 1, float fRange = 40.0, int iPlot = FALSE,
oToAttack = oPC;
}
}
iShout = TRUE;
bShout = TRUE;
}
oPC = GetNextPC();
}
@@ -275,8 +281,8 @@ void HostileAttackAllPCs(int iType = 1, float fRange = 40.0, int iPlot = FALSE,
break;
case TYPE_ALL_AREA:// is all PC's in the specific area.
{
iCnt = 1;
oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC);
nCnt = 1;
oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, OBJECT_SELF, nCnt);
while(GetIsObjectValid(oPC))
{
// Attack it! (if not a DM!)
@@ -290,19 +296,18 @@ void HostileAttackAllPCs(int iType = 1, float fRange = 40.0, int iPlot = FALSE,
oToAttack = oPC;
}
}
iShout = TRUE;
bShout = TRUE;
}
// Next one
iCnt++;
oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC,
OBJECT_SELF, iCnt);
nCnt++;
oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, OBJECT_SELF, nCnt);
}
}
break;
case TYPE_IN_RANGE:// is all PC's within fRange.
{
iCnt = 1;
oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC);
nCnt = 1;
oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, OBJECT_SELF, nCnt);
while(GetIsObjectValid(oPC) && GetDistanceToObject(oPC) <= fRange)
{
// Attack it! (if not a DM!)
@@ -316,12 +321,11 @@ void HostileAttackAllPCs(int iType = 1, float fRange = 40.0, int iPlot = FALSE,
oToAttack = oPC;
}
}
iShout = TRUE;
bShout = TRUE;
}
// Next one
iCnt++;
oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC,
OBJECT_SELF, iCnt);
nCnt++;
oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, OBJECT_SELF, nCnt);
}
}
break;
@@ -329,17 +333,22 @@ void HostileAttackAllPCs(int iType = 1, float fRange = 40.0, int iPlot = FALSE,
// Attack nearest one (if valid)
if(GetIsObjectValid(oToAttack))
{
// Change our flags for plot and immortal (usually turns them off)
SetPlotFlag(OBJECT_SELF, bPlot);
SetImmortal(OBJECT_SELF, bImmortal);
DetermineSpeakCombatRound(oToAttack);
if(iAllAllies) AlliesAttack(oToAttack);
if(bAllAllies) AlliesAttack(oToAttack);
}
// Check if we shout
if(iShout) AISpeakString(CALL_TO_ARMS);
if(bShout) AISpeakString(AI_SHOUT_CALL_TO_ARMS);
}
// This will thier most damaging melee weapon, and wait to disarm it.
// This will thier most damaging weapon, and wait to disarm it.
// * fDuration - Delay until the weapon is withdrawn.
void EquipWeaponsDuration(float fDuration, int iRanged = FALSE)
// * bRanged - if TRUE, it will equip a ranged weapon as a prioritory (EquipRanged call)
void EquipWeaponsDuration(float fDuration, int bRanged = FALSE)
{
if(iRanged)
if(bRanged)
{
// Equip any most damaging (don't use oVersus, incase it doesn't arm anything)
ActionEquipMostDamagingRanged();
@@ -376,23 +385,23 @@ void RemoveWeapons()
//::///////////////////////////////////////////////
//:: Created by : Jasperre
//:://///////////////////////////////////////////*/
void PlaySomeTaunt(int iLowest, int iHighest)
void PlaySomeTaunt(int nLowest, int nHighest)
{
int iRandom = Random(iHighest) + iLowest;
int iVoice = VOICE_CHAT_ATTACK;
switch (iRandom)
int nRandom = Random(nHighest) + nLowest;
int nVoice = VOICE_CHAT_ATTACK;
switch (nRandom)
{
case 0: iVoice = VOICE_CHAT_ATTACK; break;
case 1: iVoice = VOICE_CHAT_TAUNT; break;
case 2: iVoice = VOICE_CHAT_BATTLECRY1; break;
case 3: iVoice = VOICE_CHAT_BATTLECRY2; break;
case 4: iVoice = VOICE_CHAT_BATTLECRY3; break;
case 5: iVoice = VOICE_CHAT_ENEMIES; break;
case 6: iVoice = VOICE_CHAT_GROUP; break;
case 7: iVoice = VOICE_CHAT_HELP; break;
default: iVoice = VOICE_CHAT_ATTACK; break;
case 0: nVoice = VOICE_CHAT_ATTACK; break;
case 1: nVoice = VOICE_CHAT_TAUNT; break;
case 2: nVoice = VOICE_CHAT_BATTLECRY1; break;
case 3: nVoice = VOICE_CHAT_BATTLECRY2; break;
case 4: nVoice = VOICE_CHAT_BATTLECRY3; break;
case 5: nVoice = VOICE_CHAT_ENEMIES; break;
case 6: nVoice = VOICE_CHAT_GROUP; break;
case 7: nVoice = VOICE_CHAT_HELP; break;
default: nVoice = VOICE_CHAT_ATTACK; break;
}
PlayVoiceChat(iVoice);
PlayVoiceChat(nVoice);
}
// Gets all allies of ourselves to attack oTarget
@@ -400,16 +409,16 @@ void PlaySomeTaunt(int iLowest, int iHighest)
void AlliesAttack(object oTarget)
{
if(!GetIsObjectValid(oTarget)) return;
int iCnt = 1;
object oAlly = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, OBJECT_SELF, iCnt, CREATURE_TYPE_IS_ALIVE, TRUE);
int nCnt = 1;
object oAlly = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, OBJECT_SELF, nCnt, CREATURE_TYPE_IS_ALIVE, TRUE);
while(GetIsObjectValid(oAlly) && GetDistanceToObject(oAlly) <= 50.0)
{
// A slightly modified way to determine a combat round.
// * oTarget - The target to attack
// * oAttacker - The NPC who you want to determine a combat round, on oTarget
DetermineSpeakCombatRoundNotMe(oTarget, oAlly);
iCnt++;
oAlly = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, OBJECT_SELF, iCnt, CREATURE_TYPE_IS_ALIVE, TRUE);
nCnt++;
oAlly = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, OBJECT_SELF, nCnt, CREATURE_TYPE_IS_ALIVE, TRUE);
}
}
@@ -429,4 +438,10 @@ object GetNearestFriendCreature()
return GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND);
}
//void main(){}
// Debug: To compile this script full, uncomment all of the below.
/* - Add two "/"'s at the start of this line
void main()
{
return;
}
//*/

View File

@@ -1,6 +1,6 @@
/************************ [Include - Other AI Functions] ***********************
Filename: j_inc_other_ai
************************* [Include - Other AI Functions] ***********************
/*/////////////////////// [Include - Other AI Functions] ///////////////////////
Filename: J_INC_Other_AI
///////////////////////// [Include - Other AI Functions] ///////////////////////
This contains fuctions and calls for these scripts:
nw_c2_default2 - Percieve
nw_c2_default3 - On Combat round End (For DetermineCombatRound() only)
@@ -9,43 +9,44 @@
nw_c2_default6 - Damaged
nw_c2_default8 - Disturbed
nw_c2_defaultb - Spell cast at
Ones that don't use this use different/No includes.
Ones that don't use this use different or no includes.
HOPEFULLY it will make them faster, if they don't run combat.
They use Execute Script to initiate combat. (With the override ones
initiating the override version, the normal initiateing the normal).
************************* [History] ********************************************
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Added to speed up compilings and gather non-combat, or other workings
in one place.
************************* [Workings] *******************************************
This is included, by #include "J_INC_OTHER_AI" in other AI files.
1.4 - TO DO:
-
///////////////////////// [Workings] ///////////////////////////////////////////
This is included in other AI files.
They then use these functions in them scripts.
************************* [Arguments] ******************************************
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: N/A
************************* Include - Other AI Functions] ***********************/
///////////////////////// [Include - Other AI Functions] /////////////////////*/
// All constants.
#include "j_inc_constants"
#include "J_INC_CONSTANTS"
// Responds to it (like makinging the callers attacker thier target)
// Responds to it (like makinging the callers attacker thier target)
// Called in OnConversation, and thats it. Use "ShouterFriend" To stop repeated GetIsFriend calls.
void RespondToShout(object oShouter, int nShoutIndex);
// Gets the attacker or attakee of the target, which should be a friend
// Gets any possible target which is attacking oShouter (and isn't an ally)
// or who oShouter is attacking. oShouter should be a ally.
object GetIntruderFromShout(object oShouter);
// Shouts, or really brings all people in 60.0M(by default) to the "shouter"
void ShoutBossShout(object oEnemy);
// Checks the target for a specific EFFECT_TYPE constant value
// Returns TRUE or FALSE. Used On Damaged for polymorph checking.
int GetHasEffect(int nEffectType, object oTarget = OBJECT_SELF);
// This sets a morale penalty, to the exsisting one, if there is one.
// It will reduce itself after fDuration (or if we die, ETC, it is deleted).
// It is deleted at the end of combat as well.
void SetMoralePenalty(int iPenalty, float fDuration = 0.0);
// Removes iPenalty amount if it can.
void RemoveMoralePenalty(int iPenalty);
void SetMoralePenalty(int nPenalty, float fDuration = 0.0);
// Removes nPenalty amount if it can.
void RemoveMoralePenalty(int nPenalty);
// At 5+ intelligence, we fire off any dispells at oPlaceables location
void SearchDispells(object oPlaceable);
@@ -66,6 +67,18 @@ void ActionMoveToEnemy(object oEnemy);
// - They then run! (Badly)
int PerceptionFleeFrom(object oEnemy);
// This wrappers commonly used code for a "Call to arms" type response.
// * We know of no enemy, so we will move to oAlly, who either called to
// us, or, well, we know of.
// * Calls out AI_SHOUT_CALL_TO_ARMS too.
void CallToArmsResponse(object oAlly);
// This wrappers commonly used code for a "I was attacked" type response.
// * We know there will be an enemy - or should be - and if we find one to attack
// (using GetIntruderFromShout()) - we attack it (and call another I was attacked)
// else, this will run CallToArmsResponse(oAlly);
// * Calls out AI_SHOUT_I_WAS_ATTACKED, or AI_SHOUT_CALL_TO_ARMS too.
void IWasAttackedResponse(object oAlly);
/*::///////////////////////////////////////////////
//:: Name: ShoutBossShout
//::///////////////////////////////////////////////
@@ -74,16 +87,19 @@ int PerceptionFleeFrom(object oEnemy);
//:://///////////////////////////////////////////*/
void ShoutBossShout(object oEnemy)
{
if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_BOSS_MONSTER_SHOUT, AI_OTHER_COMBAT_MASTER))
// 1.4 - Added a 5 minute cooldown timer for this. Thusly, if the boss lingers,
// so will the big shout they do.
if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_BOSS_MONSTER_SHOUT, AI_OTHER_COMBAT_MASTER) &&
!GetLocalTimer(AI_TIMER_BOSS_SHOUT_COOLDOWN))
{
// Get the range (and default to 60.0 M)
float fRange = IntToFloat(GetBoundriedAIInteger(AI_BOSS_MONSTER_SHOUT_RANGE, i60, 370));
float fRange = IntToFloat(GetBoundriedAIInteger(AI_BOSS_MONSTER_SHOUT_RANGE, 60, 370));
// We loop through nearest not-seen, not-heard allies and get them
// to attack the person.
int Cnt = i1;
int nCnt = 1;
// Not seen, not heard...
object oAlly = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND,
OBJECT_SELF, Cnt, CREATURE_TYPE_IS_ALIVE, TRUE,
OBJECT_SELF, nCnt, CREATURE_TYPE_IS_ALIVE, TRUE,
CREATURE_TYPE_PERCEPTION, PERCEPTION_NOT_SEEN_AND_NOT_HEARD);
// Get who thier target is.
object oThierTarget;
@@ -103,13 +119,19 @@ void ShoutBossShout(object oEnemy)
ExecuteScript(COMBAT_FILE, oAlly);
}
}
Cnt++;
nCnt++;
oAlly = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND,
OBJECT_SELF, Cnt, CREATURE_TYPE_IS_ALIVE, TRUE,
OBJECT_SELF, nCnt, CREATURE_TYPE_IS_ALIVE, TRUE,
CREATURE_TYPE_PERCEPTION, PERCEPTION_NOT_SEEN_AND_NOT_HEARD);
}
// Remove it :-)
DeleteSpawnInCondition(AI_FLAG_OTHER_COMBAT_BOSS_MONSTER_SHOUT, AI_OTHER_COMBAT_MASTER);
// * Don't speak when dead. 1.4 change (an obvious one to make)
if(CanSpeak())
{
// Speak a string associated with this action being carried out
SpeakArrayString(AI_TALK_ON_LEADER_BOSS_SHOUT);
}
// Remove it for 5 minutes.
SetLocalTimer(AI_TIMER_BOSS_SHOUT_COOLDOWN, 300.0);
}
}
// This MAY make us set a local timer to turn off hiding.
@@ -122,7 +144,7 @@ void TurnOffHiding(object oIntruder)
(GetObjectSeen(OBJECT_SELF, oIntruder) ||
GetObjectHeard(OBJECT_SELF, oIntruder)))
{
SetLocalTimer(AI_TIMER_TURN_OFF_HIDE, f18);
SetLocalTimer(AI_TIMER_TURN_OFF_HIDE, 18.0);
}
}
@@ -136,7 +158,8 @@ void HideOrClear()
GetActionMode(OBJECT_SELF, ACTION_MODE_STEALTH) == FALSE)
{
// Need skill or force on
if((GetSkillRank(SKILL_HIDE) - i4 >= GetHitDice(OBJECT_SELF)) ||
int nRank = GetSkillRank(SKILL_HIDE);
if((nRank - 4 >= GetHitDice(OBJECT_SELF) && nRank >= 7) ||
GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_HIDING, AI_OTHER_COMBAT_MASTER))
{
// Use hide
@@ -173,12 +196,17 @@ void HideOrClear()
I_WAS_ATTACKED = 1;
If not in combat, attack the attackee of the shouter. Basically the best
way to get people to come and help us.
way to get people to come and help us, if we know of an attacker!
* Call this after we call DetermineCombatRound() to make sure that any
responses know of the attackers. It doesn't matter in actual fact, but
useful anyway.
CALL_TO_ARMS = 3;
If not in combat, determine combat round. By default, it should check any
allies it can see/hear for thier targets and help them too.
* Better if we do not know of a target (and thusly our allies wouldn't know
of them as well) so the allies will move to us.
HELP_MY_FRIEND = 4;
@@ -208,179 +236,254 @@ void HideOrClear()
//::///////////////////////////////////////////////
// Modified almost completely: Jasperre
//:://///////////////////////////////////////////*/
// Gets the attacker or attakee of the target, which should be a friend
// Gets any possible target which is attacking oShouter (and isn't an ally)
// or who oShouter is attacking. oShouter should be a ally.
object GetIntruderFromShout(object oShouter)
{
object oIntruder = GetAttackTarget(oShouter);
if(!GetIsObjectValid(oIntruder) ||
GetIgnoreNoFriend(oIntruder))
// First, get who they specifically want to attack (IE: Input target the shout
// is usually for)
object oIntruder = GetLocalObject(oShouter, AI_OBJECT + AI_ATTACK_SPECIFIC_OBJECT);
if(GetIgnoreNoFriend(oIntruder) || (!GetObjectSeen(oShouter) && !GetObjectHeard(oShouter)))
{
oIntruder = GetLastHostileActor(oShouter);
if(GetIgnoreNoFriend(oIntruder))
// Or, we look for the last melee target (which, at least, will be set)
oIntruder = GetLocalObject(oShouter, AI_OBJECT + AI_LAST_MELEE_TARGET);
if(GetIgnoreNoFriend(oIntruder) || (!GetObjectSeen(oShouter) && !GetObjectHeard(oShouter)))
{
return OBJECT_INVALID;
// Current actual attack target of the shouter
oIntruder = GetAttackTarget(oShouter);
if(GetIgnoreNoFriend(oIntruder) || (!GetObjectSeen(oShouter) && !GetObjectHeard(oShouter)))
{
// Last hostile actor of the shouter
oIntruder = GetLastHostileActor(oShouter);
if(GetIgnoreNoFriend(oIntruder) || (!GetObjectSeen(oShouter) && !GetObjectHeard(oShouter)))
{
return OBJECT_INVALID;
}
}
}
}
return oIntruder;
}
// Responds to it (like makinging the callers attacker thier target)
// Called in OnConversation, and thats it. Use "ShouterFriend" To stop repeated GetIsFriend calls.
void RespondToShout(object oShouter, int nShoutIndex)
{
// We use oIntruder to set who to attack.
object oIntruder;
// Ones we don't care about if we are in combat...
if(nShoutIndex == i6) // "Attack specific object"
// Check nShoutIndex against known constants
switch(nShoutIndex)
{
// If a leader, we set it as a local object, nothing more
if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_GROUP_LEADER, AI_OTHER_COMBAT_MASTER, oShouter))
// Note: Not checked in sequential order (especially as they are constants).
// Instead, it is "Ones which if we are in combat, we still check" first.
// Attack a specific object which the leader shouted about.
case AI_SHOUT_LEADER_ATTACK_TARGET_CONSTANT:
{
oIntruder = GetLocalObject(oShouter, AI_ATTACK_SPECIFIC_OBJECT);
if(GetObjectSeen(oIntruder))
// If a leader, we set it as a local object, nothing more
if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_GROUP_LEADER, AI_OTHER_COMBAT_MASTER, oShouter))
{
// Set local object to use in next DetermineCombatRound.
// We do not interrupt current acition (EG: Life saving stoneskins!) to re-direct.
SetAIObject(AI_ATTACK_SPECIFIC_OBJECT, oIntruder);
// 6 second delay.
SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, f6);
// 70. "[Shout] Reacting To Shout. [ShoutNo.] " + IntToString(iInput) + " [Shouter] " + GetName(oInput)
DebugActionSpeakByInt(70, oShouter, nShoutIndex);
oIntruder = GetLocalObject(oShouter, AI_ATTACK_SPECIFIC_OBJECT);
if(GetObjectSeen(oIntruder))
{
// Set local object to use in next DetermineCombatRound.
// We do not interrupt current acition (EG: Life saving stoneskins!) to re-direct.
SetAIObject(AI_ATTACK_SPECIFIC_OBJECT, oIntruder);
// 6 second delay.
SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, 6.0);
}
}
return;
}
return;
}
else if(nShoutIndex == i5)// "leader flee now"
{
// If a leader, we set it as a local object, nothing more
if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_GROUP_LEADER, AI_OTHER_COMBAT_MASTER, oShouter))
break;
// Leader flee now - mass retreat to those who hear it.
case AI_SHOUT_LEADER_FLEE_NOW_CONSTANT:
{
oIntruder = GetLocalObject(oShouter, AI_FLEE_TO);
// RUN! If intruder set is over 5.0M or no valid intruder
ClearAllActions();
// If a leader, we set it as a local object, nothing more
if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_GROUP_LEADER, AI_OTHER_COMBAT_MASTER, oShouter))
{
// Get who we are going to run too
oIntruder = GetLocalObject(oShouter, AI_FLEE_TO);
// RUN! If intruder set is over 5.0M or no valid intruder
ClearAllActions();
// 70. "[Shout] Reacting To Shout. [ShoutNo.] " + IntToString(iInput) + " [Shouter] " + GetName(oInput)
DebugActionSpeakByInt(70, oShouter, nShoutIndex);
// Set to run
SetCurrentAction(AI_SPECIAL_ACTIONS_FLEE);
// Turn on fleeing visual effect
ApplyFleeingVisual();
// Ignore talk for 12 seconds
SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, 12.0);
// If valid, we run to the intruder
if(GetIsObjectValid(oIntruder))
{
SetAIObject(AI_FLEE_TO, oIntruder);
ActionMoveToObject(oIntruder);
}
else // Else, we will just follow our leader!
{
SetAIObject(AI_FLEE_TO, oShouter);
ActionForceFollowObject(oShouter, 3.0);
}
}
return;
}
break;
// All others (IE: We need to not be in combat for these)
// Anything that requires "DetermineCombatRound()" is here.
// If the shout is number 8, it is "I was opened" and so can only be a
// placeable or door.
case AI_SHOUT_I_WAS_OPENED_CONSTANT:
{
// If we are already attacking, we ignore this shout.
if(CannotPerformCombatRound()) return;
// We need somewhat complexe here - to get thier opener.
int nType = GetObjectType(oShouter);
// Check object type. If not a placeable nor door - stop script.
if(nType == OBJECT_TYPE_PLACEABLE ||
nType == OBJECT_TYPE_DOOR)
{
// 70. "[Shout] Reacting To Shout. [ShoutNo.] " + IntToString(iInput) + " [Shouter] " + GetName(oInput)
DebugActionSpeakByInt(70, oShouter, nShoutIndex);
// Now, we assign the placeable/door to set thier opener.
// We do this by just executing a script that does it.
ExecuteScript(FILE_SET_OPENER, oShouter);
// We can immediantly get this would-be attacker!
oIntruder = GetLocalObject(oShouter, AI_PLACEABLE_LAST_OPENED_BY);
if(GetIsObjectValid(oIntruder))
{
// Attack
ClearAllActions();
DetermineCombatRound(oShouter);
}
else
{
// Move to the object who shouted in detect mode
SetActionMode(OBJECT_SELF, ACTION_MODE_DETECT, TRUE);
ActionMoveToObject(oShouter, TRUE);
}
}
return;
}
break;
// Call to arms requires nothing special. It is only called if
// There is no target the shouter has to attack specifically, rather then
// "I_WAS_ATTACKED" which would have.
case AI_SHOUT_CALL_TO_ARMS_CONSTANT:
{
// If we are already attacking, we ignore this shout.
if(CannotPerformCombatRound()) return;
// Ignore for 6 seconds
SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, 6.0);
// 70. "[Shout] Reacting To Shout. [ShoutNo.] " + IntToString(iInput) + " [Shouter] " + GetName(oInput)
DebugActionSpeakByInt(70, oShouter, nShoutIndex);
SetCurrentAction(AI_SPECIAL_ACTIONS_FLEE);
SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, f12);
if(GetIsObjectValid(oIntruder))
{
SetAIObject(AI_FLEE_TO, oIntruder);
ActionMoveToObject(oIntruder);
}
else // Else, we will just follow our leader!
{
SetAIObject(AI_FLEE_TO, oShouter);
ActionForceFollowObject(oShouter, f3);
}
// do standard Call to Arms response - IE: Move to oShouter
CallToArmsResponse(oShouter);
return;
}
return;
}
// If the shout is number 8, it is "I was opened" and so can only be a
// placeable or door.
else if(nShoutIndex == i8)// "I was opened"
{
// We need somewhat complexe here - to get thier opener.
int nType = GetObjectType(oShouter);
// Check object type. If not a placeable nor door - stop script.
if(nType == OBJECT_TYPE_PLACEABLE ||
nType == OBJECT_TYPE_DOOR)
{
// Now, we assign the placeable/door to set thier opener.
// - Need to check it works.
AssignCommand(oShouter, SetLocalObject(oShouter, PLACEABLE_LAST_OPENED_BY, GetLastOpenedBy()));
oIntruder = GetLocalObject(oShouter, PLACEABLE_LAST_OPENED_BY);
if(GetIsObjectValid(oIntruder))
{
// Attack
ClearAllActions();
DetermineCombatRound(oShouter);
}
}
}
// Else, we must not be in combat for the rest
else if(!CannotPerformCombatRound())
{
// Call to arms requires nothing special
if(nShoutIndex == i3)// "Call to arms"
{
SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, f6);
// 70. "[Shout] Reacting To Shout. [ShoutNo.] " + IntToString(iInput) + " [Shouter] " + GetName(oInput)
DebugActionSpeakByInt(70, oShouter, nShoutIndex);
DetermineCombatRound();
}
// Ones we can GetIntruderFromShout(oShouter);
if(nShoutIndex == i1 || // "I was attacked"
nShoutIndex == i4 || // "Help my friend"
nShoutIndex == i7) // "I was killed"
{
// Am not already fighting, and we don't ignore the intruder
oIntruder = GetIntruderFromShout(oShouter);
if(!GetIsObjectValid(oIntruder))
{
return;
}
}
if(nShoutIndex == i1 ||
nShoutIndex == i7)
{
// Morale penalty if they were killed
if(nShoutIndex == i7)
{
SetMoralePenalty((GetHitDice(oShouter)/i4), f18);
}
// Get intruder
// 70. "[Shout] Reacting To Shout. [ShoutNo.] " + IntToString(iInput) + " [Shouter] " + GetName(oInput)
DebugActionSpeakByInt(70, oShouter, nShoutIndex);
if(GetObjectSeen(oIntruder))
{
// Stop, and attack, if we can see them!
ClearAllActions();
SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, f9);
DetermineCombatRound(oIntruder);
DelayCommand(f2, AISpeakString(I_WAS_ATTACKED));
}
else // Else the enemy is not seen
{
// If I can see neither the shouter nor the enemy
// stop what I am doing, and move to the attacker.
// - 1.3 change. They move to the attackers location (IE directed by ally)
ClearAllActions();
SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, f6);
// This will move to oIntruder if nothing else
DetermineCombatRound(oIntruder);
// Shout to other allies, after a second.
DelayCommand(f2, AISpeakString(HELP_MY_FRIEND));
}
}
else if(nShoutIndex == i4)// "Help my friend"
break;
// "Help my friend" is when a runner is running off (sorta fleeing) to
// get help. This will move to the location set on them to reinforce.
case AI_SHOUT_HELP_MY_FRIEND_CONSTANT:
{
// If we are already attacking, we ignore this shout.
if(CannotPerformCombatRound()) return;
// Ignore things for 6 seconds
SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, 6.0);
// We move to where the runner/shouter wants us.
location lMoveTo = GetLocalLocation(oShouter, AI_HELP_MY_FRIEND_LOCATION);
location lMoveTo = GetLocalLocation(oShouter, AI_LOCATION + AI_HELP_MY_FRIEND_LOCATION);
// 70. "[Shout] Reacting To Shout. [ShoutNo.] " + IntToString(iInput) + " [Shouter] " + GetName(oInput)
DebugActionSpeakByInt(70, oShouter, nShoutIndex);
SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, f6);
// If the location is valid
if(GetIsObjectValid(GetAreaFromLocation(lMoveTo)))
{
// New special action, but one that is overrided by combat
SetCurrentAction(AI_SPECIAL_ACTIONS_MOVE_TO_COMBAT);
SetAIObject(AI_MOVE_TO_COMBAT_OBJECT, oShouter);
SetAILocation(AI_MOVE_TO_COMBAT_LOCATION, lMoveTo);
// Move to the location of the fight, attack.
ClearAllActions();
// Move to the fights location
ActionMoveToLocation(lMoveTo, TRUE);
ActionDoCommand(DetermineCombatRound());
// When we see someone fighting, we'll DCR
return;
}
else
{
// If we do not know of the friend attacker, we will follow them
// Else, if we do not know of the friends attackers, or the location
// they are at, we will follow them without casting any preperation
// spells.
ClearAllActions();
SetSpawnInCondition(AI_FLAG_COMBAT_FLAG_FAST_BUFF_ENEMY, AI_COMBAT_MASTER);
ActionForceFollowObject(oShouter, f3);
ActionDoCommand(DetermineCombatRound());
ActionForceFollowObject(oShouter, 3.0);
// When we see an enemy, we'll attack!
return;
}
return;
}
break;
// "I was attacked" is called when a creature is hurt or sees an enemy,
// and starts to attack them. This means they know who the enemy is -
// and thusly we can get it from them (Ususally GetLastHostileActor()
// "I was killed" is the same, but applies a morale penalty too
case AI_SHOUT_I_WAS_ATTACKED_CONSTANT:
case AI_SHOUT_I_WAS_KILLED_CONSTANT:
{
// If it was "I was killed", we apply a short morale penatly
// Penalty is "Hit dice / 4 + 1" (so always 1 minimum) for 18 seconds.
if(nShoutIndex == AI_SHOUT_I_WAS_KILLED_CONSTANT)
{
SetMoralePenalty(GetHitDice(oShouter)/4 + 1, 18.0);
}
// If we are already attacking, we ignore this shout.
if(CannotPerformCombatRound()) return;
// 70. "[Shout] Reacting To Shout. [ShoutNo.] " + IntToString(iInput) + " [Shouter] " + GetName(oInput)
DebugActionSpeakByInt(70, oShouter, nShoutIndex);
// Ignore for 6 seconds
SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, 6.0);
// Respond to oShouter's request for help - find thier target, and
// attack!
IWasAttackedResponse(oShouter);
return;
}
break;
}
}
// At 5+ intelligence, we fire off any dispells at oPlaceables location
void SearchDispells(object oPlaceable)
{
// No dispelling at low intelligence.
if(GetBoundriedAIInteger(AI_INTELLIGENCE) < i5) return;
if(GetBoundriedAIInteger(AI_INTELLIGENCE) < 5) return;
location lPlace = GetLocation(oPlaceable);
// Move closer if not seen.
if(!GetObjectSeen(oPlaceable))
{
// Move nearer - 6 M is out of the dispell range
ActionMoveToObject(oPlaceable, TRUE, f6);
ActionMoveToObject(oPlaceable, TRUE, 6.0);
}
// Dispell if we have any - at the location of oPlaceable.
if(GetHasSpell(SPELL_LESSER_DISPEL))
@@ -401,41 +504,22 @@ void SearchDispells(object oPlaceable)
}
}
// Get Has Effect
// Checks to see if the target has a given
// effect, usually from a spell. Really useful this is.
int GetHasEffect(int nEffectType, object oTarget = OBJECT_SELF)
{
effect eCheck = GetFirstEffect(oTarget);
while(GetIsEffectValid(eCheck))
{
if(GetEffectType(eCheck) == nEffectType)
{
return TRUE;
break;
}
eCheck = GetNextEffect(oTarget);
}
return FALSE;
}
// This sets a morale penalty, to the exsisting one, if there is one.
// It will reduce itself (by the penalty) after fDuration (or if we die, ETC, it is deleted).
// It is deleted at the end of combat as well.
void SetMoralePenalty(int iPenalty, float fDuration = 0.0)
void SetMoralePenalty(int nPenalty, float fDuration = 0.0)
{
int iOriginal = GetAIInteger(AI_MORALE_PENALTY);
int iNew = iOriginal + iPenalty;
SetAIInteger(AI_MORALE_PENALTY, iNew);
DelayCommand(fDuration, RemoveMoralePenalty(iPenalty));
int nNewPenalty = GetAIInteger(AI_MORALE_PENALTY) + nPenalty;
SetAIInteger(AI_MORALE_PENALTY, nNewPenalty);
DelayCommand(fDuration, RemoveMoralePenalty(nPenalty));
}
void RemoveMoralePenalty(int iPenalty)
// Removes nPenalty amount if it can.
void RemoveMoralePenalty(int nPenalty)
{
int iOriginal = GetAIInteger(AI_MORALE_PENALTY);
int iNew = iOriginal - iPenalty;
if(iNew > 0 && !GetIsDead(OBJECT_SELF))
int nNewPenalty = GetAIInteger(AI_MORALE_PENALTY) - nPenalty;
if(nNewPenalty > 0 && !GetIsDead(OBJECT_SELF))
{
SetAIInteger(AI_MORALE_PENALTY, iNew);
SetAIInteger(AI_MORALE_PENALTY, nNewPenalty);
}
else
{
@@ -478,10 +562,10 @@ int PerceptionFleeFrom(object oEnemy)
// Valid run from target
if(!GetIsObjectValid(oRunTarget))
{
oRunTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE);
oRunTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE);
if(!GetIsObjectValid(oRunTarget))
{
oRunTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD, CREATURE_TYPE_IS_ALIVE, TRUE);
oRunTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD, CREATURE_TYPE_IS_ALIVE, TRUE);
if(!GetIsObjectValid(oRunTarget))
{
oRunTarget = GetLastHostileActor();
@@ -495,12 +579,140 @@ int PerceptionFleeFrom(object oEnemy)
}
// Run from enemy
ClearAllActions();
ActionMoveAwayFromObject(oRunTarget, TRUE, f50);
ActionMoveAwayFromObject(oRunTarget, TRUE, 50.0);
return TRUE;
}
// 0 or more morale.
return FALSE;
}
// This wrappers commonly used code for a "Call to arms" type response.
// * We know of no enemy, so we will move to oAlly, who either called to
// us, or, well, we know of.
// * Calls out AI_SHOUT_CALL_TO_ARMS too.
void CallToArmsResponse(object oAlly)
{
// Shout to allies to attack, or be prepared.
AISpeakString(AI_SHOUT_CALL_TO_ARMS);
// If we are over 2 meters away from oShouter, we move to them using
// the special action
if(GetDistanceToObject(oAlly) > 2.0 || !GetObjectSeen(oAlly))
{
// New special action, but one that is overrided by combat
location lAlly = GetLocation(oAlly);
SetCurrentAction(AI_SPECIAL_ACTIONS_MOVE_TO_COMBAT);
SetAIObject(AI_MOVE_TO_COMBAT_OBJECT, oAlly);
SetAILocation(AI_MOVE_TO_COMBAT_LOCATION, lAlly);
// Move to the location of the fight, attack.
ClearAllActions();
// Move to the fights location
ActionMoveToLocation(lAlly, TRUE);
// When we see someone fighting, we'll DCR
return;
}
else
{
// Determine it anyway - we will search around oShouter
// if nothing is found...but we are near to the shouter
DetermineCombatRound(oAlly);
return;
}
}
// This wrappers commonly used code for a "I was attacked" type response.
// * We know there will be an enemy - or should be - and if we find one to attack
// (using GetIntruderFromShout()) - we attack it (and call another I was attacked)
// else, this will run CallToArmsResponse(oAlly);
// * Calls out AI_SHOUT_I_WAS_ATTACKED, or AI_SHOUT_CALL_TO_ARMS too.
void IWasAttackedResponse(object oAlly)
{
// Get the indruder. This is either who oShouter is currently attacking,
// or the last attacker of them.
object oIntruder = GetIntruderFromShout(oAlly);
// If valid, of course attack!
if(GetIsObjectValid(oIntruder))
{
// 1.4 Note:
// * It used to check "Are they seen". Basically, this is redudant
// with the checks used in DetermineCombatRound(). It will do the
// searching using oIntruder whatever.
// Stop, and attack
ClearAllActions();
DetermineCombatRound(oIntruder);
// Shout I was attacked - we've set our intruder now
AISpeakString(AI_SHOUT_I_WAS_ATTACKED);
return;
}
// If invalid, we act as if it was "Call to arms" type thing.
// Call to arms is better to use normally, of course.
else
{
// Shout to allies to attack, or be prepared.
AISpeakString(AI_SHOUT_CALL_TO_ARMS);
// We see if they are attacking anything:
oIntruder = GetAttackTarget(oAlly);
if(!GetIsObjectValid(oIntruder))
{
oIntruder = GetLocalObject(oAlly, AI_OBJECT + AI_LAST_MELEE_TARGET);
}
// If valid, we will move to a point bisecting the intruder and oAlly, or
// move to oAlly. Should get interrupted once we see the attack target.
// * NEED TO TEST
if(GetIsObjectValid(oIntruder))
{
// New special action, but one that is overrided by combat
vector vTarget = GetPosition(oIntruder);
vector vSource = GetPosition(OBJECT_SELF);
vector vDirection = vTarget - vSource;
float fDistance = VectorMagnitude(vDirection) / 2.0;
vector vPoint = VectorNormalize(vDirection) * fDistance + vSource;
location lTarget = Location(GetArea(OBJECT_SELF), vPoint, DIRECTION_NORTH);
SetCurrentAction(AI_SPECIAL_ACTIONS_MOVE_TO_COMBAT);
SetAIObject(AI_MOVE_TO_COMBAT_OBJECT, oAlly);
SetAILocation(AI_MOVE_TO_COMBAT_LOCATION, lTarget);
// Move to the location of the fight, attack.
ClearAllActions();
// Move to the fights location
ActionMoveToLocation(lTarget, TRUE);
// When we see someone fighting, we'll DCR
return;
}
// If we are over 2 meters away from oShouter, we move to them using
// the special action
else if(GetDistanceToObject(oAlly) > 2.0 || !GetObjectSeen(oAlly))
{
// New special action, but one that is overrided by combat
location lAlly = GetLocation(oAlly);
SetCurrentAction(AI_SPECIAL_ACTIONS_MOVE_TO_COMBAT);
SetAIObject(AI_MOVE_TO_COMBAT_OBJECT, oAlly);
SetAILocation(AI_MOVE_TO_COMBAT_LOCATION, lAlly);
// Move to the location of the fight, attack.
ClearAllActions();
// Move to the fights location
ActionMoveToLocation(lAlly, TRUE);
// When we see someone fighting, we'll DCR
return;
}
else
{
// Determine it anyway - we will search around oShouter
// if nothing is found...but we are near to the shouter
DetermineCombatRound(oAlly);
return;
}
}
}
// Debug: To compile this script full, uncomment all of the below.
/* - Add two "/"'s at the start of this line

View File

@@ -1,6 +1,6 @@
/************************ [Set Effects Include] ********************************
Filename:
************************* [Set Effects] ****************************************
/*/////////////////////// [Include - Set Effects] //////////////////////////////
Filename: J_INC_SetEffects
///////////////////////// [Include - Set Effects] //////////////////////////////
This can be executed on a PC or NPC, and sets what thier current effects
are - the hostile ones.
@@ -9,22 +9,28 @@
It is meant to be more efficient then doing countless checks against other
NPCs and PCs for what effects they already have on them.
************************* [History] ********************************************
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Added
************************* [Workings] *******************************************
1.4 - Changed so fear and stun was seperate.
Uncommandable still is now for any uncommandable effects.
- TO DO
- Make the ability decrease into "light" "major" and so on, about 4
ratings, each set as more decreases are present.
///////////////////////// [Workings] ///////////////////////////////////////////
ExecuteScript - might not work faster. If so, it is easy to add into the
generic AI and have oTarget to set to.
It searches the code and sets 3 custom integers, but only once (so not
during the loop)
************************* [Arguments] ******************************************
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: N/A
************************* [Set Effects] ***************************************/
///////////////////////// [Include - Set Effects] ////////////////////////////*/
#include "J_INC_CONSTANTS"
// List (use Global to not conflict with the nwscript.nss!)
const int GlobalEffectUncommandable = 0x00000001;// Stun. Sleep. Fear. Turning.
const int GlobalEffectUncommandable = 0x00000001;// Stun. Sleep. Fear. Turning. Paralsis. Petrify.
const int GlobalEffectSilenced = 0x00000002;// Eeek!
const int GlobalEffectSlowed = 0x00000004;// Stop with haste.
const int GlobalEffectUltravision = 0x00000008;
@@ -35,7 +41,7 @@ const int GlobalEffectInvisible = 0x00000080;
const int GlobalEffectDeaf = 0x00000100;// Ack!
const int GlobalEffectHaste = 0x00000200;
const int GlobalEffectPolymorph = 0x00000400;// Only attack
const int GlobalEffectBlindness = 0x00000800;// Oh no!
const int GlobalEffectBlindness = 0x00000800;// Oh no! Cannot see others to cast spells.
const int GlobalEffectDisease = 0x00001000;
const int GlobalEffectPoison = 0x00002000;
const int GlobalEffectCurse = 0x00004000;
@@ -47,13 +53,15 @@ const int GlobalEffectDazed = 0x00080000;// Special: 1.30
const int GlobalEffectEthereal = 0x00100000;
const int GlobalEffectPetrify = 0x00200000;
const int GlobalEffectParalyze = 0x00400000;// Divided from Uncommandable for healing of
//const int GlobalEffectAbilityDecrease = 0x00080000;// Ohh! Tingly!
const int GlobalEffectSpellFailure = 0x00800000;// Makes sure spells are not cast under high failure.
const int GlobalEffectDamageShield = 0x01000000;// All damage shields
const int GlobalEffectFear = 0x02000000;// 1.4. Remove fear + G.Rest. Removes.
const int GlobalEffectStun = 0x04000000;// 1.4. G.Rest. Removes.
//int GlobalEffectAbilityDecrease = 0; // In combat include
// These are Globals for spell effects, to not csat them on us again, and to
// speed things up...
// These are *good* spells. This effect is only set up on us.
const int GlobalHasStoneSkinProtections = 0x00000001;
const int GlobalHasElementalProtections = 0x00000002;
const int GlobalHasVisageProtections = 0x00000004;
@@ -80,7 +88,7 @@ const int GlobalHasRegenerateSpell = 0x00400000;
const int GlobalHasOwlsWisdomSpell = 0x00800000;
const int GlobalHasSpellResistanceSpell = 0x01000000;
const int GlobalHasSpellWarCrySpell = 0x02000000;
//const int GlobalHasElementalShieldSpell = 0x04000000;
//const int GlobalHasElementalShieldSpell = 0x04000000;// Is general effect
const int GlobalHasDomainSpells = 0x08000000;
const int GlobalHasDeflectionACSpell = 0x10000000;
const int GlobalHasNaturalACSpell = 0x20000000;
@@ -94,154 +102,173 @@ const int GlobalHasWeaponHelpSpell = 0x80000000;
int TempEffectHex, TempSpellHex;
// Sets up an effects thing to that
void AI_SetWeHaveEffect(int iEffectHex);
void AI_SetTargetHasEffect(int nEffectHex);
// Sets we have spell iSpellHex's effects.
void AI_SetWeHaveSpellsEffect(int iSpellHex);
void AI_SetWeHaveSpellsEffect(int nSpellHex);
// Sets up effects on oTarget
void AI_SetEffectsOnTarget(object oTarget = OBJECT_SELF);
// Simple return TRUE if it matches hex.
// - Effects tested on oTarget
int AI_GetAIHaveEffect(int iEffectHex, object oTarget = OBJECT_SELF);
int AI_GetAIHaveEffect(int nEffectHex, object oTarget = OBJECT_SELF);
// Simple return TRUE if it matches hex.
// - Uses oTarget
int AI_GetAIHaveSpellsEffect(int iSpellHex, object oTarget = OBJECT_SELF);
// * Can only be used on ourself.
int AI_GetAIHaveSpellsEffect(int nSpellHex);
// Sets up an effects thing to that
void AI_SetWeHaveEffect(int iEffectHex)
void AI_SetTargetHasEffect(int nEffectHex)
{
TempEffectHex = TempEffectHex | iEffectHex;
TempEffectHex = TempEffectHex | nEffectHex;
}
// Sets we have spell iSpellHex's effects.
void AI_SetWeHaveSpellsEffect(int iSpellHex)
void AI_SetWeHaveSpellsEffect(int nSpellHex)
{
TempSpellHex = TempSpellHex | iSpellHex;
TempSpellHex = TempSpellHex | nSpellHex;
}
// Simple return TRUE if it matches hex.
// - Effects tested on oTarget
int AI_GetAIHaveEffect(int iEffectHex, object oTarget = OBJECT_SELF)
int AI_GetAIHaveEffect(int nEffectHex, object oTarget = OBJECT_SELF)
{
return (GetLocalInt(oTarget, AI_EFFECT_HEX) & iEffectHex);
return (GetLocalInt(oTarget, AI_EFFECT_HEX) & nEffectHex);
}
// Simple return TRUE if it matches hex.
// - Uses oTarget
int AI_GetAIHaveSpellsEffect(int iSpellHex, object oTarget = OBJECT_SELF)
// * Can only be used on ourself.
int AI_GetAIHaveSpellsEffect(int nSpellHex)
{
return (GetLocalInt(oTarget, AI_SPELL_HEX) & iSpellHex);
return (GetLocalInt(OBJECT_SELF, AI_SPELL_HEX) & nSpellHex);
}
// Sets up effects on oTarget
void AI_SetEffectsOnTarget(object oTarget = OBJECT_SELF)
{
TempEffectHex = FALSE;
TempSpellHex = FALSE;
// Checks our effects once.
effect eCheck = GetFirstEffect(oTarget);
int iEffect, iEffectAbilityDecrease, iSpellID;
int nEffect, nEffectAbilityDecrease, nSpellID;
// EFFECTS:
// For ALL targets (that we will use), we set up effects on a system of Hexes.
// like spawn in things. Replaces GetHasSpellEffect, except genralising -
// IE we will NOT cast more than one of the stoneskin type things at once.
while(GetIsEffectValid(eCheck))
{
iEffect = GetEffectType(eCheck);
switch(iEffect)
nEffect = GetEffectType(eCheck);
switch(nEffect)
{
case EFFECT_TYPE_INVALIDEFFECT:
case EFFECT_TYPE_VISUALEFFECT:
// Don't check these for spell values.
break;
case EFFECT_TYPE_PARALYZE:
AI_SetWeHaveEffect(GlobalEffectParalyze);
case EFFECT_TYPE_PARALYZE: // Also makes you uncommandable
{
AI_SetTargetHasEffect(GlobalEffectParalyze);
AI_SetTargetHasEffect(GlobalEffectUncommandable);
}
break;
case EFFECT_TYPE_STUNNED:
case EFFECT_TYPE_FRIGHTENED:
case EFFECT_TYPE_STUNNED: // Also makes you uncommandable
{
AI_SetTargetHasEffect(GlobalEffectStun);
AI_SetTargetHasEffect(GlobalEffectUncommandable);
}
break;
case EFFECT_TYPE_FRIGHTENED: // Also makes you uncommandable
{
AI_SetTargetHasEffect(GlobalEffectFear);
AI_SetTargetHasEffect(GlobalEffectUncommandable);
}
break;
// * Cannot remove these, but make you unable to move.
case EFFECT_TYPE_SLEEP:
case EFFECT_TYPE_TURNED:
case EFFECT_TYPE_DISAPPEARAPPEAR:// Added for dragon flying
AI_SetWeHaveEffect(GlobalEffectUncommandable);
case EFFECT_TYPE_CONFUSED:// 1.4 added. wasn't in before
AI_SetTargetHasEffect(GlobalEffectUncommandable);
break;
case EFFECT_TYPE_DAZED:
AI_SetWeHaveEffect(GlobalEffectDazed);
AI_SetTargetHasEffect(GlobalEffectDazed);
break;
case EFFECT_TYPE_SILENCE:
AI_SetWeHaveEffect(GlobalEffectSilenced);
AI_SetTargetHasEffect(GlobalEffectSilenced);
break;
case EFFECT_TYPE_SLOW:
AI_SetWeHaveEffect(GlobalEffectSlowed);
AI_SetTargetHasEffect(GlobalEffectSlowed);
break;
case EFFECT_TYPE_ULTRAVISION:
AI_SetWeHaveEffect(GlobalEffectUltravision);
AI_SetTargetHasEffect(GlobalEffectUltravision);
break;
case EFFECT_TYPE_SEEINVISIBLE:
AI_SetWeHaveEffect(GlobalEffectSeeInvisible);
AI_SetTargetHasEffect(GlobalEffectSeeInvisible);
break;
// Caused by Beholder things mainly, but this stops any spell being
// cast, not just, for example, arcane spells cast in armor.
case EFFECT_TYPE_SPELL_FAILURE:
AI_SetWeHaveEffect(GlobalEffectSpellFailure);
AI_SetTargetHasEffect(GlobalEffectSpellFailure);
break;
// Penetrates darkness.
case EFFECT_TYPE_TRUESEEING:
AI_SetWeHaveEffect(GlobalEffectTrueSeeing);
AI_SetTargetHasEffect(GlobalEffectTrueSeeing);
break;
// Timestop - IE don't cast same spell twice.
case EFFECT_TYPE_TIMESTOP:
AI_SetWeHaveEffect(GlobalEffectTimestop);
AI_SetTargetHasEffect(GlobalEffectTimestop);
break;
// Invisibility/Improved (although improved only uses normal in the spell)
// Sneak attack/whatever :-)
// - include the spell EFFECT_TYPE_ETHEREAL.
case EFFECT_TYPE_INVISIBILITY:
case EFFECT_TYPE_IMPROVEDINVISIBILITY:
AI_SetWeHaveEffect(GlobalEffectInvisible);
AI_SetTargetHasEffect(GlobalEffectInvisible);
break;
// Deaf - spell failing of 20%, but still cast.
case EFFECT_TYPE_DEAF:
AI_SetWeHaveEffect(GlobalEffectDeaf);
AI_SetTargetHasEffect(GlobalEffectDeaf);
break;
// Special invis.
case EFFECT_TYPE_ETHEREAL:
AI_SetWeHaveEffect(GlobalEffectEthereal);
AI_SetTargetHasEffect(GlobalEffectEthereal);
break;
// Haste - so don't cast haste again and whatever.
case EFFECT_TYPE_HASTE:
AI_SetWeHaveEffect(GlobalEffectHaste);
AI_SetTargetHasEffect(GlobalEffectHaste);
break;
// Haste - so don't cast haste again and whatever.
case EFFECT_TYPE_POLYMORPH:
AI_SetWeHaveEffect(GlobalEffectPolymorph);
AI_SetTargetHasEffect(GlobalEffectPolymorph);
break;
// Blindness - oh no, can't see, only hear!
case EFFECT_TYPE_BLINDNESS:
AI_SetWeHaveEffect(GlobalEffectBlindness);
AI_SetTargetHasEffect(GlobalEffectBlindness);
break;
// Damage shield = Elemental shield, wounding whispers, Death armor, mestals
// sheth and so on.
case EFFECT_TYPE_ELEMENTALSHIELD:
AI_SetWeHaveEffect(GlobalEffectDamageShield);
AI_SetTargetHasEffect(GlobalEffectDamageShield);
break;
// Things we may want to remove VIA cirtain spells, we set here - may as well.
// Same setting as any other.
// IF we can remove it (not confusion ETC of course) then we set it.
case EFFECT_TYPE_DISEASE:
AI_SetWeHaveEffect(GlobalEffectDisease);
AI_SetTargetHasEffect(GlobalEffectDisease);
break;
case EFFECT_TYPE_POISON:
AI_SetWeHaveEffect(GlobalEffectPoison);
AI_SetTargetHasEffect(GlobalEffectPoison);
break;
// SoU Petrify
// Note: Also makes them uncommandable
case EFFECT_TYPE_PETRIFY:
AI_SetWeHaveEffect(GlobalEffectPetrify);
{
AI_SetTargetHasEffect(GlobalEffectPetrify);
AI_SetTargetHasEffect(GlobalEffectUncommandable);
}
break;
case EFFECT_TYPE_CURSE:
AI_SetWeHaveEffect(GlobalEffectCurse);
AI_SetTargetHasEffect(GlobalEffectCurse);
break;
case EFFECT_TYPE_NEGATIVELEVEL:
AI_SetWeHaveEffect(GlobalEffectNegativeLevel);
AI_SetTargetHasEffect(GlobalEffectNegativeLevel);
break;
case EFFECT_TYPE_ABILITY_DECREASE:
case EFFECT_TYPE_AC_DECREASE:
@@ -252,24 +279,25 @@ void AI_SetEffectsOnTarget(object oTarget = OBJECT_SELF)
case EFFECT_TYPE_SPELL_RESISTANCE_DECREASE:
case EFFECT_TYPE_SKILL_DECREASE:
// Special - we add one to this, to determine when to use restoration
iEffectAbilityDecrease++;
nEffectAbilityDecrease++;
break;
case EFFECT_TYPE_ENTANGLE:
AI_SetWeHaveEffect(GlobalEffectEntangle);
AI_SetTargetHasEffect(GlobalEffectEntangle);
break;
case EFFECT_TYPE_MOVEMENT_SPEED_DECREASE:
AI_SetWeHaveEffect(GlobalEffectMovementSpeedDecrease);
AI_SetTargetHasEffect(GlobalEffectMovementSpeedDecrease);
break;
case EFFECT_TYPE_DARKNESS:
AI_SetWeHaveEffect(GlobalEffectDarkness);
AI_SetTargetHasEffect(GlobalEffectDarkness);
break;
default:
{
// Check spells we have on...so we don't cast over them!
iSpellID = GetEffectSpellId(eCheck);
if(iSpellID != iM1)
// Check spells *we* (1.4 change: now checks OBJECT_SELF)
// have on...so we don't cast over them!
nSpellID = GetEffectSpellId(eCheck);
if(nSpellID != -1 && oTarget == OBJECT_SELF)
{
switch(iSpellID)
switch(nSpellID)
{
// All weapon things are on one variable. We cast the best.
case SPELL_MAGIC_WEAPON:
@@ -338,7 +366,7 @@ void AI_SetEffectsOnTarget(object oTarget = OBJECT_SELF)
break;
case SPELL_CATS_GRACE:
case SPELL_GREATER_CATS_GRACE:
case AI_SPELL_HARPER_CATS_GRACE: // Harper
case AI_SPELLABILITY_HARPER_CATS_GRACE: // Harper
AI_SetWeHaveSpellsEffect(GlobalHasCatsGraceSpell);
break;
case SPELL_CLAIRAUDIENCE_AND_CLAIRVOYANCE:
@@ -361,7 +389,7 @@ void AI_SetEffectsOnTarget(object oTarget = OBJECT_SELF)
break;
case SPELL_EAGLE_SPLEDOR:
case SPELL_GREATER_EAGLE_SPLENDOR:
case AI_SPELL_HARPER_EAGLE_SPLEDOR: // Harper
case AI_SPELLABILITY_HARPER_EAGLE_SPLEDOR: // Harper
AI_SetWeHaveSpellsEffect(GlobalHasEaglesSpledorSpell);
break;
case SPELL_ENDURANCE:
@@ -436,9 +464,13 @@ void AI_SetEffectsOnTarget(object oTarget = OBJECT_SELF)
}
DeleteLocalInt(oTarget, AI_ABILITY_DECREASE);
DeleteLocalInt(oTarget, AI_EFFECT_HEX);
DeleteLocalInt(oTarget, AI_SPELL_HEX);
// Special - only we set spell hexs on ourselves.
if(oTarget == OBJECT_SELF)
{
DeleteLocalInt(oTarget, AI_SPELL_HEX);
SetLocalInt(oTarget, AI_SPELL_HEX, TempSpellHex);
}
// Set final ones from temp integers
SetLocalInt(oTarget, AI_ABILITY_DECREASE, iEffectAbilityDecrease);
SetLocalInt(oTarget, AI_ABILITY_DECREASE, nEffectAbilityDecrease);
SetLocalInt(oTarget, AI_EFFECT_HEX, TempEffectHex);
SetLocalInt(oTarget, AI_SPELL_HEX, TempSpellHex);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,7 @@ int StartingConditional()
object oPC = GetPCSpeaker();
int nInt;
nInt=GetLocalInt(oPC, "NW_JOURNAL_ENTRYmarthek);
nInt = GetLocalInt(oPC, "NW_JOURNAL_ENTRYmarthek");
if (nInt >= 1)
return TRUE;

View File

@@ -7,14 +7,13 @@ http://nwvault.ign.com/View.php?view=Other.Detail&id=4683&id=625 */
//Put this on action taken in the conversation editor
void main()
{
object oPC = GetPCSpeaker();
object oItem;
oItem = GetItemPossessedBy(oPC, "ScrollofEtherealJaunt");
if (GetIsObjectValid(oItem))
DestroyObject(oItem);
object oPC = GetPCSpeaker();
object oTarget;
location lTarget;
oTarget = GetWaypointByTag("menak1");

View File

@@ -1,6 +1,6 @@
/************************ [On Heartbeat] ***************************************
Filename: nw_c2_default1 or j_ai_onheartbeat
************************* [On Heartbeat] ***************************************
/*/////////////////////// [On Heartbeat] ///////////////////////////////////////
Filename: nw_c2_default1 or J_AI_OnHeartbeat
///////////////////////// [On Heartbeat] ///////////////////////////////////////
Removed stupid stuff, special behaviour, sleep.
Also, note please, I removed waypoints and day/night posting from this.
@@ -24,21 +24,22 @@
execute script.
-Working- Best possible, fast compile.
************************* [History] ********************************************
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Added more "buffs" to fast buff.
- Fixed animations (they both WORK and looping ones do loop right!)
- Loot behaviour!
- Randomly moving nearer a PC in 25M if set.
- Removed silly day/night optional setting. Anything we can remove, is a good idea.
************************* [Workings] *******************************************
1.4 - Removed AI level setting. Not good to use, I mistakenly added it.
///////////////////////// [Workings] ///////////////////////////////////////////
This fires off every 6 seconds (with PCs in the area, or AI_LEVEL_HIGH without)
and therefore is intensive.
It fires of ExecutesScript things for the different parts - saves CPU stuff
if the bits are not used.
************************* [Arguments] ******************************************
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: Basically, none. Nothing activates this script. Fires every 6 seconds.
************************* [On Heartbeat] **************************************/
///////////////////////// [On Heartbeat] /////////////////////////////////////*/
// - This includes J_Inc_Constants
#include "J_INC_HEARTBEAT"
@@ -52,10 +53,8 @@ void main()
// - Includes door bashing stop heartbeat
if(PerformSpecialAction()) return;
// Pre-heartbeat-event
if(FireUserEvent(AI_FLAG_UDE_HEARTBEAT_PRE_EVENT, EVENT_HEARTBEAT_PRE_EVENT))
// We may exit if it fires
if(ExitFromUDE(EVENT_HEARTBEAT_PRE_EVENT)) return;
// Pre-heartbeat-event. Returns TRUE if we interrupt this script call.
if(FirePreUserEvent(AI_FLAG_UDE_HEARTBEAT_PRE_EVENT, EVENT_HEARTBEAT_PRE_EVENT)) return;
// AI status check. Is the AI on?
if(GetAIOff() || GetSpawnInCondition(AI_FLAG_OTHER_LAG_IGNORE_HEARTBEAT, AI_OTHER_MASTER)) return;
@@ -63,29 +62,6 @@ void main()
// Define the enemy and player to use.
object oEnemy = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY);
object oPlayer = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC);
int iTempInt;
// AI level (re)setting
if(!GetIsInCombat() && !GetIsObjectValid(GetAttackTarget()) &&
(GetIsObjectValid(oEnemy) && GetDistanceToObject(oEnemy) <= f50 ||
GetIsObjectValid(oPlayer) && GetDistanceToObject(oPlayer) <= f50))
{
// AI setting, normally higher then normal.
iTempInt = GetAIConstant(LAG_AI_LEVEL_YES_PC_OR_ENEMY_50M);
if(iTempInt > iM1 && GetAILevel() != iTempInt)
{
SetAILevel(OBJECT_SELF, iTempInt);
}
}
else
{
// AI setting, normally higher then normal.
iTempInt = GetAIConstant(LAG_AI_LEVEL_NO_PC_OR_ENEMY_50M);
if(iTempInt > iM1 && GetAILevel() != iTempInt)
{
SetAILevel(OBJECT_SELF, iTempInt);
}
}
// We can skip to the end if we are in combat, or something...
if(!JumpOutOfHeartBeat() && // We don't stop due to effects.
@@ -95,7 +71,7 @@ void main()
{
// Fast buffing...if we have the spawn in condition...
if(GetSpawnInCondition(AI_FLAG_COMBAT_FLAG_FAST_BUFF_ENEMY, AI_COMBAT_MASTER) &&
GetIsObjectValid(oEnemy) && GetDistanceToObject(oEnemy) <= f40)
GetIsObjectValid(oEnemy) && GetDistanceToObject(oEnemy) <= 40.0)
{
// ...we may do an advanced buff. If we cannot see/hear oEnemy, but oEnemy
// is within 40M, we cast many defensive spells instantly...
@@ -116,21 +92,20 @@ void main()
// We must have animations set, and not be "paused", so doing a
// longer looping one
// - Need a valid player.
if(GetIsObjectValid(oPlayer))
if(GetIsObjectValid(oPlayer) && !IsInConversation(OBJECT_SELF))
{
// Do we have any animations to speak of?
// If we have a nearby PC, not in conversation, we do animations.
if(!IsInConversation(OBJECT_SELF) &&
GetAIInteger(AI_VALID_ANIMATIONS))
{
ExecuteScript(FILE_HEARTBEAT_ANIMATIONS, OBJECT_SELF);
}
// We may search for PC enemies :-) move closer to PC's
else if(GetSpawnInCondition(AI_FLAG_OTHER_SEARCH_IF_ENEMIES_NEAR, AI_OTHER_MASTER) &&
!GetLocalTimer(AI_TIMER_SEARCHING) && d4() == i1)
// We may search for PC enemies, 25% chance to move closer to PC's
if(GetSpawnInCondition(AI_FLAG_OTHER_SEARCH_IF_ENEMIES_NEAR, AI_OTHER_MASTER) &&
!GetLocalTimer(AI_TIMER_SEARCHING) && d4() == 1)
{
ExecuteScript(FILE_HEARTBEAT_WALK_TO_PC, OBJECT_SELF);
}
// Else, Do we have any animations to speak of?
// If we have a nearby PC, we do animations.
else if(GetHasValidAnimations())
{
ExecuteScript(FILE_HEARTBEAT_ANIMATIONS, OBJECT_SELF);
}
}
}
}

View File

@@ -1,6 +1,6 @@
/************************ [On Percieve] ****************************************
Filename: j_ai_onpercieve or nw_c2_default2
************************* [On Percieve] ****************************************
/*/////////////////////// [On Percieve] ////////////////////////////////////////
Filename: J_AI_OnPercieve or nw_c2_default2
///////////////////////// [On Percieve] ////////////////////////////////////////
If the target is an enemy, attack
Will determine combat round on that person, is an enemy, basically.
Includes shouting for a big radius - if the spawn in condition is set to this.
@@ -8,37 +8,43 @@
NOTE: Debug strings in this file will be uncommented for speed by default.
- It is one of the most intensive scripts as it runs so often.
- Attempted to optimise as much as possible.
************************* [History] ********************************************
///////////////////////// [History] ////////////////////////////////////////////
1.3 - We include j_inc_other_ai to initiate combat (or go into combat again)
- j_inc_other_ai holds all other needed functions/integers ETC.
- Turn off hide things.
- Added "Only attack if attacked"
- Removed special conversation things. Almost no one uses them, and the taunt system is easier.
- Should now search around if they move to a dead body, and only once they get there.
************************* [Workings] *******************************************
1.4 - TO DO:
1. Perception needs checking - attacking outside perception ranges!
2. Vanishing targets, etc. test, improve.
3. Problems with dispelling invisibility. Maybe either do change the line to create placable, or, of course, cast at location (dispells cannot be metamagiked or whatever) Source
4. No Effect Type Ethereal. Source
///////////////////////// [Workings] ///////////////////////////////////////////
It fires:
- When a creature enters it perception range (Set in creature properties) and
is seen or heard.
* Tests show (and in general) it fires HEARD first, then immediantly SEEN if,
of course, they are visible. Odd really, but true.
- When a creature uses invisiblity/leaves the area in the creatures perception
range
- When a creature appears suddenly, already in the perception range (not
the other way round, normally)
- When a creature moves out of the creatures perception range, and therefore
becomes unseen.
************************* [Arguments] ******************************************
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: GetLastPerceived, GetLastPerceptionSeen, GetLastPerceptionHeard,
GetLastPerceptionVanished, GetLastPerceptionInaudible.
************************* [On Percieve] ***************************************/
///////////////////////// [On Percieve] //////////////////////////////////////*/
#include "j_inc_other_ai"
#include "J_INC_OTHER_AI"
void main()
{
// Percieve pre event.
if(FireUserEvent(AI_FLAG_UDE_PERCIEVE_PRE_EVENT, EVENT_PERCIEVE_PRE_EVENT))
// We may exit if it fires
if(ExitFromUDE(EVENT_PERCIEVE_PRE_EVENT)) return;
// Pre-percieve-event. Returns TRUE if we interrupt this script call.
if(FirePreUserEvent(AI_FLAG_UDE_PERCIEVE_PRE_EVENT, EVENT_PERCIEVE_PRE_EVENT)) return;
// AI status check. Is the AI on?
if(GetAIOff()) return;
@@ -47,8 +53,21 @@ void main()
// - We declare OUTSIDE if's JUST IN CASE!
object oPerceived = GetLastPerceived();
object oAttackTarget = GetAttackTarget();
// 1.4: Very rarely is our attack target valid, so we will set it to
// what we would have melee attacked when DCR was called.
if(GetIgnoreNoFriend(oAttackTarget) || oAttackTarget == OBJECT_SELF)
{
oAttackTarget = GetAIObject(AI_LAST_MELEE_TARGET);
}
int bSeen = GetLastPerceptionSeen();
int bHeard = GetLastPerceptionHeard();
int bVanished = GetLastPerceptionVanished();
int bInaudiable = GetLastPerceptionInaudible();
// Debug
DebugActionSpeak("*** PER ***: " + GetName(oPerceived) + "| SEEN: " + IntToString(bSeen) +
"| HEARD: " + IntToString(bHeard) + "| VANISHED: " + IntToString(bVanished) +
"| INAUDIABLE: " + IntToString(bInaudiable));
// Need to be valid and not ignorable.
if(GetIsObjectValid(oPerceived) &&
@@ -58,31 +77,35 @@ void main()
// First, easy enemy checks.
if(GetIsEnemy(oPerceived) && !GetFactionEqual(oPerceived))
{
DebugActionSpeak("*** PER *** ENEMY");
// 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(oPerceived);
// Well, are we both inaudible and vanished?
// * the GetLastPerception should only say what specific event has fired!
if(GetLastPerceptionInaudible() || GetLastPerceptionVanished())
if(bVanished || bInaudiable)
{
DebugActionSpeak("*** PER *** VANISHED OR INAUDIBLE");
// If they just became invisible because of the spell
// invisiblity, or improved invisiblity...we set a local object.
// - Beta: Added in ethereal as well.
if(GetHasEffect(EFFECT_TYPE_INVISIBILITY, oPerceived) ||
GetHasEffect(EFFECT_TYPE_ETHEREAL, oPerceived) ||
GetHasEffect(EFFECT_TYPE_SANCTUARY, oPerceived) ||
GetStealthMode(oPerceived) == STEALTH_MODE_ACTIVATED)
{
// Set object, AND the location they went invisible!
SetAIObject(AI_LAST_TO_GO_INVISIBLE, oPerceived);
// We also set thier location for AOE dispelling - same name
SetLocalLocation(OBJECT_SELF, AI_LAST_TO_GO_INVISIBLE, GetLocation(oPerceived));
SetAILocation(AI_LAST_TO_GO_INVISIBLE, GetLocation(oPerceived));
}
// If they were our target, follow! >:-D
// - Optional, on spawn option, for following through areas.
if(oAttackTarget == oPerceived)
{
DebugActionSpeak("*** PER *** VANISHED OR INAUDIBLE AND IS CURRENT TARGET");
// This means they have exited the area! follow!
if(GetArea(oPerceived) != GetArea(OBJECT_SELF))
{
@@ -94,7 +117,10 @@ void main()
}
// - Added check for not casting a spell. If we are, we finnish
// (EG: AOE spell) and automatically carry on.
else if(GetCurrentAction() != ACTION_CASTSPELL)
// 1.4: If we are using a targetted spell, we do cancle our
// spellcasting if it is them.
else if(GetCurrentAction() != ACTION_CASTSPELL ||
GetAttackTarget() == oPerceived)
{
ClearAllActions();
// 52: "[Perception] Enemy Vanished (Same area) Retargeting/Searching [Percieved] " + GetName(oPerceived)
@@ -111,76 +137,79 @@ void main()
if(bSeen && GetCurrentAction() != ACTION_CASTSPELL &&
(oAttackTarget == oPerceived || !GetObjectSeen(oAttackTarget)))
{
AISpeakString(I_WAS_ATTACKED);
// 53: "[Perception] Enemy seen, and was old enemy/cannot see current. Re-evaluating (no spell) [Percieved] " + GetName(oPerceived)
DebugActionSpeakByInt(53, oPerceived);
DetermineCombatRound(oPerceived);
// Shout to allies to attack oPerceived
AISpeakString(AI_SHOUT_I_WAS_ATTACKED);
}
// Else We check if we are already attacking.
else if(!CannotPerformCombatRound() &&
!GetSpawnInCondition(AI_FLAG_OTHER_ONLY_ATTACK_IF_ATTACKED, AI_OTHER_MASTER))
{
// Special shout, d1000 though!
SpeakArrayString(AI_TALK_ON_PERCIEVE_ENEMY, TRUE);
// * Don't speak when dead. 1.4 change (an obvious one to make)
if(CanSpeak())
{
// Special shout, d1000 though!
SpeakArrayString(AI_TALK_ON_PERCIEVE_ENEMY, TRUE);
}
// Stop stuff because of facing point - New enemy
HideOrClear();
// Face the person (this helps stops sneak attacks if we then
// cast something on ourselves, ETC).
SetFacingPoint(GetPosition(oPerceived));
// Get all allies in 60M to come to thier aid. Talkvolume silent
// shout does not seem to work well.
// - void function. Checks for the spawn int. in it.
// - Turns it off in it too
// - Turns it off in it too (5 minutes - 1.4)
// - Variable range On Spawn
ShoutBossShout(oPerceived);
// Warn others
AISpeakString(I_WAS_ATTACKED);
// 54: "[Perception] Enemy Seen. Not in combat, attacking. [Percieved] " + GetName(oPerceived)
DebugActionSpeakByInt(54, oPerceived);
// Note: added ActionDoCommand, so that SetFacingPoint is not
// interrupted.
ActionDoCommand(DetermineCombatRound(oPerceived));
// 1.4 change: SetFacingPoint(GetPosition(oPerceived));
// Is now part of DetermineCombatRound().
// * This means other events will work similarily.
DetermineCombatRound(oPerceived);
// Warn others
AISpeakString(AI_SHOUT_I_WAS_ATTACKED);
}
}
}
// Else, they are an equal faction, or not an enemy (or both)
else
{
// If they are dead, say we saw something on waypoints, we charge in
// to investigate.
// If they are dead, or fighting, eg: we saw something on
// waypoints, we charge in to investigate.
// * Higher intelligence will buff somewhat as well!
if(GetIsDead(oPerceived) && (bSeen || bHeard))
if((GetIsDead(oPerceived) || GetIsInCombat(oPerceived)) &&
(bSeen || bHeard))
{
// Warn others
AISpeakString(I_WAS_ATTACKED);
if(GetIsDead(oPerceived))
{
// 55: "[Perception] Percieved Dead Friend! Moving into investigate [Percieved] " + GetName(oPerceived)
DebugActionSpeakByInt(55, oPerceived);
}
else
{
// 56: "[Perception] Percieved Alive Fighting Friend! Moving to and attacking. [Percieved] " + GetName(oPerceived)
DebugActionSpeakByInt(56, oPerceived);
}
// Check if we can attack
if(!CannotPerformCombatRound())
{
// Hide or clear actions
HideOrClear();
// 55: "[Perception] Percieved Dead Friend! Moving and Searching [Percieved] " + GetName(oPerceived)
DebugActionSpeakByInt(55, oPerceived);
ActionMoveToLocation(GetLocation(oPerceived), TRUE);
ActionDoCommand(DetermineCombatRound());
// If we were called to arms, react
CallToArmsResponse(oPerceived);
}
}
else if(GetIsInCombat(oPerceived) && (bSeen || bHeard))
{
// Warn others
AISpeakString(I_WAS_ATTACKED);
// Only if we can attack.
if(!CannotPerformCombatRound())
else
{
// Hide or clear actions
HideOrClear();
// 56: "[Perception] Percieved Alive Fighting Friend! Moving to and attacking. [Percieved] " + GetName(oPerceived)
DebugActionSpeakByInt(56, oPerceived);
ActionMoveToLocation(GetLocation(oPerceived), TRUE);
ActionDoCommand(DetermineCombatRound());
// Warn others even if we don't, or cannot, attack
AISpeakString(AI_SHOUT_CALL_TO_ARMS);
}
}
}

View File

@@ -1,28 +1,27 @@
/************************ [On Combat Round End] ********************************
Filename: nw_c2_default3 or j_ai_oncombatrou
************************* [On Combat Round End] ********************************
/*/////////////////////// [On Combat Round End] ////////////////////////////////
Filename: nw_c2_default3 or J_AI_OnCombatrou
///////////////////////// [On Combat Round End] ////////////////////////////////
This is run every 3 or 6 seconds, if the creature is in combat. It is
executed only in combat automatically.
It runs what the AI should do, bascially.
************************* [History] ********************************************
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Executes same script as the other parts of the AI to cuase a new action
************************* [Workings] *******************************************
1.4 -
///////////////////////// [Workings] ///////////////////////////////////////////
Calls the combat AI file using the J_INC_OTHER_AI include function,
DetermineCombatRound.
************************* [Arguments] ******************************************
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: GetAttackTarget, GetLastHostileActor, GetAttemptedAttackTarget,
GetAttemptedSpellTarget (Or these are useful at least!)
************************* [On Combat Round End] *******************************/
///////////////////////// [On Combat Round End] //////////////////////////////*/
#include "j_inc_other_ai"
#include "J_INC_OTHER_AI"
void main()
{
// Pre-combat-round-event
if(FireUserEvent(AI_FLAG_UDE_END_COMBAT_ROUND_PRE_EVENT, EVENT_END_COMBAT_ROUND_PRE_EVENT))
// We may exit if it fires
if(ExitFromUDE(EVENT_END_COMBAT_ROUND_PRE_EVENT)) return;
// Pre-combat-round-event. Returns TRUE if we interrupt this script call.
if(FirePreUserEvent(AI_FLAG_UDE_END_COMBAT_ROUND_PRE_EVENT, EVENT_END_COMBAT_ROUND_PRE_EVENT)) return;
// AI status check. Is the AI on?
if(GetAIOff()) return;

View File

@@ -1,16 +1,17 @@
/************************ [On Conversation] ************************************
Filename: j_ai_onconversat or nw_c2_default4
************************* [On Conversation] ************************************
/*/////////////////////// [On Conversation] ////////////////////////////////////
Filename: J_AI_OnConversat or nw_c2_default4
///////////////////////// [On Conversation] ////////////////////////////////////
OnConversation/ Listen to shouts.
Documented, and checked. -Working-
Added spawn in condition - Never clear actions when talking.
************************* [History] ********************************************
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Added in conversation thing - IE we can set speakstrings, no need for conversation file.
- Sorted more shouts out.
- Should work right, and not cause too many actions (as we ignore
shouts for normally 12 or so seconds before letting them affect us again).
************************* [Workings] *******************************************
1.4 - Deafness incorpreated.
///////////////////////// [Workings] ///////////////////////////////////////////
Uses RespondToShout to react to allies' shouts, and just attacks any enemy
who speaks, or at least moves to them. (OK, dumb if they are invisible, but
oh well, they shouldn't talk so loud!)
@@ -18,19 +19,21 @@
Remember, whispers are never heard if too far away, speakstrings don't go
through walls, and shouts are always heard (so we don't go off to anyone
not in our area, remember)
************************* [Arguments] ******************************************
Deafness causes us to never hear battle, so unless we see the target speaking
we do not react. Doesn't apply to normal conversations - although if we cannot
talk (also restricted by deafness) then so be it.
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: GetListenPatternNumber, GetLastSpeaker, TestStringAgainstPattern,
GetMatchedSubstring
************************* [On Conversation] ***********************************/
///////////////////////// [On Conversation] //////////////////////////////////*/
#include "j_inc_other_ai"
#include "J_INC_OTHER_AI"
void main()
{
// Pre-heartbeat-event
if(FireUserEvent(AI_FLAG_UDE_ON_DIALOGUE_PRE_EVENT, EVENT_ON_DIALOGUE_PRE_EVENT))
// We may exit if it fires
if(ExitFromUDE(EVENT_ON_DIALOGUE_PRE_EVENT)) return;
// Pre-conversation-event. Returns TRUE if we interrupt this script call.
if(FirePreUserEvent(AI_FLAG_UDE_ON_DIALOGUE_PRE_EVENT, EVENT_ON_DIALOGUE_PRE_EVENT)) return;
// AI status check. Is the AI on?
if(GetAIOff()) return;
@@ -38,17 +41,16 @@ void main()
// Declarations
int nMatch = GetListenPatternNumber();
object oShouter = GetLastSpeaker();
string sSpoken = GetMatchedSubstring(i0);
string sSpoken = GetMatchedSubstring(0);
// We can ignore everything under special cases - EG no valid shouter,
// we are fleeing, its us, or we are not in the same area.
// - We break out of the script if this happens.
if(!GetIsObjectValid(oShouter) || /* Must be a valid speaker! */
!GetCommandable() || /* Commandable */
oShouter == OBJECT_SELF || /* Not us! */
GetIsPerformingSpecialAction() || /* Not fleeing */
GetIgnore(oShouter) || /* Not ignoring the shouter */
GetArea(oShouter) != GetArea(OBJECT_SELF))/* Same area (Stops "SHOUT" getting NPCs */
GetArea(oShouter) != GetArea(OBJECT_SELF))/* Same area (Stops loud yellow shouts getting NPCs) */
{
// Fire End of Dialogue event
FireUserEvent(AI_FLAG_UDE_ON_DIALOGUE_EVENT, EVENT_ON_DIALOGUE_EVENT);
@@ -56,44 +58,51 @@ void main()
}
// Conversation if not a shout.
if(nMatch == iM1)
if(nMatch == -1)
{
// Make sure it is a PC and we are not fighting.
if(!GetIsFighting() && (GetIsPC(oShouter) || GetIsDMPossessed(oShouter)))
// * Don't speak when dead. 1.4 change (an obvious one to make)
if(CanSpeak())
{
// If we have something random (or not) to say instead of
// the conversation, we will say that.
if(GetLocalInt(OBJECT_SELF, ARRAY_SIZE + AI_TALK_ON_CONVERSATION))
// Make sure it is a PC and we are not fighting.
if(!GetIsFighting() && (GetIsPC(oShouter) || GetIsDMPossessed(oShouter)))
{
ClearAllActions();// Stop
SetFacingPoint(GetPosition(oShouter));// Face
SpeakArrayString(AI_TALK_ON_CONVERSATION);// Speak string
PlayAnimation(ANIMATION_LOOPING_TALK_NORMAL, f1, f3);// "Talk", then resume potitions.
ActionDoCommand(ExecuteScript(FILE_WALK_WAYPOINTS, OBJECT_SELF));
}
else
{
// If we are set to NOT clear all actions, we won't.
if(!GetSpawnInCondition(AI_FLAG_OTHER_NO_CLEAR_ACTIONS_BEFORE_CONVERSATION, AI_OTHER_MASTER))
// If we have something random (or not) to say instead of
// the conversation, we will say that.
if(GetLocalInt(OBJECT_SELF, ARRAY_SIZE + AI_TALK_ON_CONVERSATION))
{
ClearAllActions();
ClearAllActions();// Stop
SetFacingPoint(GetPosition(oShouter));// Face
SpeakArrayString(AI_TALK_ON_CONVERSATION);// Speak string
PlayAnimation(ANIMATION_LOOPING_TALK_NORMAL, 1.0, 3.0);// "Talk", then resume potitions.
ActionDoCommand(ExecuteScript(FILE_WALK_WAYPOINTS, OBJECT_SELF));
}
else
{
// If we are set to NOT clear all actions, we won't.
if(!GetSpawnInCondition(AI_FLAG_OTHER_NO_CLEAR_ACTIONS_BEFORE_CONVERSATION, AI_OTHER_MASTER))
{
ClearAllActions();
}
BeginConversation();
}
BeginConversation();
}
}
}
// If it is a valid shout...and a valid shouter.
// - Not a DM. Not ignoring shouting. Not a Debug String.
else if(!GetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID) && // Not listening (IE heard already)
!GetIsDM(oShouter) && FindSubString(sSpoken, "[Debug]") == iM1)
else if(!GetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID) &&// Not listening (IE heard already)
!GetIsDM(oShouter) && FindSubString(sSpoken, "[Debug]") == -1 &&
// 1.4 - Deafness (or they are seen) check, for fun.
(!GetHasEffect(EFFECT_TYPE_DEAF) || GetObjectSeen(oShouter)))
{
if(GetIsFriend(oShouter) || GetFactionEqual(oShouter))
{
// If they are a friend, not a PC, and a valid number, react.
// In the actual RespondToShout call, we do check to see if we bother.
// - Is PC - or is...master?
// - Shouts of 1 or over only.
if(nMatch >= i1 && !GetIsPC(oShouter) && !GetIsPC(GetMaster(oShouter)))
// - Shouts which are not negative, and not AI_ANYTHING_SAID_CONSTANT.
if(nMatch >= 0 && nMatch != AI_SHOUT_ANYTHING_SAID_CONSTANT &&
!GetIsPC(oShouter) && !GetIsPC(GetMaster(oShouter)))
{
// Respond to the shout
RespondToShout(oShouter, nMatch);
@@ -104,31 +113,27 @@ void main()
GetIsInCombat(oShouter) &&
GetObjectType(oShouter) == OBJECT_TYPE_CREATURE)
{
// Only use attack target.
object oIntruder = GetIntruderFromShout(oShouter);
// Valid, and not a friend if a PC speaker
if(GetIsObjectValid(oIntruder) &&
!GetFactionEqual(oIntruder) &&
!GetIsFriend(oIntruder))
{
// 57: "[Shout] Friend (may be PC) in combat. Attacking! [Friend] " + GetName(oShouter)
DebugActionSpeakByInt(57, oShouter);
DetermineCombatRound(oIntruder);
}
// 57: "[Shout] Friend (may be PC) in combat. Attacking! [Friend] " + GetName(oShouter)
DebugActionSpeakByInt(57, oShouter);
// Respond to oShouter
IWasAttackedResponse(oShouter);
}
}
else if(GetIsEnemy(oShouter) && GetObjectType(oShouter) == OBJECT_TYPE_CREATURE)
{
// If we hear anything said by an enemy, and are not fighting, attack them!
if(!CannotPerformCombatRound())
// the negatives are associate shouts, 0+ are my shouts. 0 is anything
// the negatives are associate shouts, Normally (!)
// 0+ are my shouts. 0 is anything
{
// We make sure it isn't an emote (set by default)
if(nMatch == i0 && GetSpawnInCondition(AI_FLAG_OTHER_DONT_RESPOND_TO_EMOTES, AI_OTHER_MASTER))
if(nMatch == AI_SHOUT_ANYTHING_SAID_CONSTANT &&
GetSpawnInCondition(AI_FLAG_OTHER_DONT_RESPOND_TO_EMOTES, AI_OTHER_MASTER))
{
// Jump out if its an emote - "*Nods*"
if(GetStringLeft(sSpoken, i1) == EMOTE_STAR &&
GetStringRight(sSpoken, i1) == EMOTE_STAR)
if(GetStringLeft(sSpoken, 1) == EMOTE_STAR &&
GetStringRight(sSpoken, 1) == EMOTE_STAR)
{
// Fire End of Dialogue event
FireUserEvent(AI_FLAG_UDE_ON_DIALOGUE_EVENT, EVENT_ON_DIALOGUE_EVENT);
@@ -137,11 +142,16 @@ void main()
}
// 58: "[Shout] Responding to shout [Enemy] " + GetName(oShouter) + " Who has spoken!"
DebugActionSpeakByInt(58, oShouter);
// Short non-respond
SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, f6);
SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, 6.0);
// Attack the enemy!
ClearAllActions();
DetermineCombatRound(oShouter);
// Shout to allies to attack the shouter
AISpeakString(AI_SHOUT_I_WAS_ATTACKED);
}
}
}

View File

@@ -1,26 +1,25 @@
/************************ [On Phisical Attacked] *******************************
Filename: j_ai_onphiattack or nw_c2_default5
************************* [On Phisical Attacked] *******************************
/*/////////////////////// [On Phisical Attacked] ///////////////////////////////
Filename: J_AI_OnPhiAttack or nw_c2_default5
///////////////////////// [On Phisical Attacked] ///////////////////////////////
On Attacked
No checking for fleeing or warnings.
Very boring really!
************************* [History] ********************************************
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Added hiding things
************************* [Workings] *******************************************
1.4 - Missing Silent Shouts have been added.
///////////////////////// [Workings] ///////////////////////////////////////////
Got some simple Knockdown timer, so that we use heal sooner if we keep getting
a creature who is attempting to knock us down.
************************* [Arguments] ******************************************
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: GetLastAttacker, GetLastWeaponUsed, GetLastAttackMode, GetLastAttackType
************************* [On Phisical Attacked] ******************************/
///////////////////////// [On Phisical Attacked] /////////////////////////////*/
#include "j_inc_other_ai"
#include "J_INC_OTHER_AI"
void main()
{
// Pre-attacked-event
if(FireUserEvent(AI_FLAG_UDE_ATTACK_PRE_EVENT, EVENT_ATTACK_PRE_EVENT))
// We may exit if it fires
if(ExitFromUDE(EVENT_ATTACK_PRE_EVENT)) return;
// Pre-attacked-event. Returns TRUE if we interrupt this script call.
if(FirePreUserEvent(AI_FLAG_UDE_ATTACK_PRE_EVENT, EVENT_ATTACK_PRE_EVENT)) return;
// AI status check. Is the AI on?
if(GetAIOff()) return;
@@ -28,11 +27,11 @@ void main()
// Set up objects.
object oAttacker = GetLastAttacker();
object oWeapon = GetLastWeaponUsed(oAttacker);
int iMode = GetLastAttackMode(oAttacker); // Currently unused
int iAttackType = GetLastAttackType(oAttacker);
//int nMode = GetLastAttackMode(oAttacker); // Currently unused
int nAttackType = GetLastAttackType(oAttacker);
if(GetIsObjectValid(oAttacker) && !GetFactionEqual(oAttacker) &&
!GetIsDM(oAttacker) && !GetIgnore(oAttacker))
// Check if they are valid, a DM, we are ignoring them, they are dead now, or invalid
if(!GetIgnoreNoFriend(oAttacker))
{
// Adjust automatically if set.
if(GetSpawnInCondition(AI_FLAG_OTHER_CHANGE_FACTIONS_TO_HOSTILE_ON_ATTACK, AI_OTHER_MASTER))
@@ -42,23 +41,28 @@ void main()
AdjustReputation(oAttacker, OBJECT_SELF, -100);
}
}
// If we were attacked by knockdown, for a timer of X seconds, we make
// sure we attempt healing at a higher level.
if(!GetLocalTimer(AI_TIMER_KNOCKDOWN) &&
(iAttackType == SPECIAL_ATTACK_IMPROVED_KNOCKDOWN ||
iAttackType == SPECIAL_ATTACK_KNOCKDOWN) &&
(nAttackType == SPECIAL_ATTACK_IMPROVED_KNOCKDOWN ||
nAttackType == SPECIAL_ATTACK_KNOCKDOWN) &&
!GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_KNOCKDOWN) &&
GetBaseAttackBonus(oAttacker) + i20 >= GetAC(OBJECT_SELF))
GetBaseAttackBonus(oAttacker) + 20 >= GetAC(OBJECT_SELF))
{
SetLocalTimer(AI_TIMER_KNOCKDOWN, f30);
SetLocalTimer(AI_TIMER_KNOCKDOWN, 30.0);
}
// Set last hostile attacker.
// Set last hostile, valid attacker (Used in the On Damaged event)
SetAIObject(AI_STORED_LAST_ATTACKER, oAttacker);
// Speak the phisically attacked string, if applicable.
// Speak the damaged string, if applicable.
SpeakArrayString(AI_TALK_ON_PHISICALLY_ATTACKED);
// * Don't speak when dead. 1.4 change (an obvious one to make)
if(CanSpeak())
{
// Speak the phisically attacked string, if applicable.
// Speak the damaged string, if applicable.
SpeakArrayString(AI_TALK_ON_PHISICALLY_ATTACKED);
}
// 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.
@@ -79,19 +83,52 @@ void main()
// If we are not fighting, and they are in the area, attack. Else, determine anyway.
if(!CannotPerformCombatRound())
{
// Must be in our area to go after now.
if(GetArea(oAttacker) == GetArea(OBJECT_SELF))
{
// 59: "[Phisically Attacked] Attacking back. [Attacker(enemy)] " + GetName(oAttacker)
DebugActionSpeakByInt(59, oAttacker);
// Attack the attacker
DetermineCombatRound(oAttacker);
// Shout to allies to attack the enemy who attacked me
AISpeakString(AI_SHOUT_I_WAS_ATTACKED);
}
else
{
// Shout to allies to attack, or be prepared.
AISpeakString(AI_SHOUT_CALL_TO_ARMS);
// 60: "[Phisically Attacked] Not same area. [Attacker(enemy)] " + GetName(oAttacker)
DebugActionSpeakByInt(60, oAttacker);
DetermineCombatRound();// May find another hostile to attack...
// May find another hostile to attack...
DetermineCombatRound();
}
}
else
{
// Shout to allies to attack, or be prepared.
AISpeakString(AI_SHOUT_CALL_TO_ARMS);
}
}
// Else, invalid, DM, ally, etc...must be prepared at least (could be
// they are charmed or something!)
else
{
// If we are not fighting, prepare for battle. Something bad must have
// happened.
if(!CannotPerformCombatRound())
{
// Respond to oAttacker as if they shouted for help.
IWasAttackedResponse(oAttacker);
}
else
{
// Shout to allies to attack, or be prepared.
AISpeakString(AI_SHOUT_CALL_TO_ARMS);
}
}
// Fire End of Attacked event

View File

@@ -1,53 +1,61 @@
/************************ [On Damaged] *****************************************
Filename: nw_c2_default6 or j_ai_ondamaged
************************* [On Damaged] *****************************************
/*/////////////////////// [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] ********************************************
///////////////////////// [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)
************************* [Workings] *******************************************
GetDamageDealtByType() will not work with proper phisical attacks - a workaround
is GetTotalDamageDealt() - All GetDamageDealtByType(). This means it will
get everything not applied with EffectDamage() which normally fires this
script.
************************* [Arguments] ******************************************
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] ****************************************/
///////////////////////// [On Damaged] ///////////////////////////////////////*/
#include "J_INC_OTHER_AI"
#include "j_inc_other_ai"
void main()
{
// Pre-damaged-event
if(FireUserEvent(AI_FLAG_UDE_DAMAGED_PRE_EVENT, EVENT_DAMAGED_PRE_EVENT))
// We may exit if it fires
if(ExitFromUDE(EVENT_DAMAGED_PRE_EVENT)) return;
// 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 iDamage = GetTotalDamageDealt();
int nDamage = GetTotalDamageDealt();
object oDamager = GetLastDamager();
// Check to see if we will polymorph.
int iPolymorph = GetAIConstant(AI_POLYMORPH_INTO);
int nPolymorph = GetAIConstant(AI_POLYMORPH_INTO);
// Total up the physical damage
// Polymorph check.
if(iPolymorph >= i0)
if(nPolymorph >= 0)
{
if(!GetHasEffect(EFFECT_TYPE_POLYMORPH))// We won't polymorph if already so
// We won't polymorph if already so
if(!GetHasEffect(EFFECT_TYPE_POLYMORPH))
{
effect eShape = SupernaturalEffect(EffectPolymorph(iPolymorph));
// 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));
}
SetAIConstant(AI_POLYMORPH_INTO, -1);// We set it to invalid (sets to 0).
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)
@@ -55,15 +63,16 @@ void main()
// 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(iDamage > i1)
if(nDamage > 0)
{
// Set it to object to string, which we will delete later anywho.
SetAIInteger(ObjectToString(oDamager), iDamage);
SetAIInteger(ObjectToString(oDamager), nDamage);
}
}
// Hostile attacker...
else if(GetIsObjectValid(oDamager) && !GetFactionEqual(oDamager) &&
!GetIsDM(oDamager)&& !GetIgnore(oDamager))
// 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))
@@ -73,80 +82,101 @@ void main()
AdjustReputation(oDamager, OBJECT_SELF, -100);
}
}
// 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).
// Need to have protection single (IE including elemental ones) ready.
if(GetSpawnInCondition(AI_VALID_TALENT_BENEFICIAL_PROTECTION_SINGLE, AI_VALID_SPELLS))
{
// Please make sure | works! By Blind Io!
int iDamageDone = GetDamageDealtByType(DAMAGE_TYPE_ACID |
DAMAGE_TYPE_COLD | DAMAGE_TYPE_ELECTRICAL |
DAMAGE_TYPE_FIRE | DAMAGE_TYPE_SONIC);
if(iDamageDone > GetAIInteger(MAX_ELEMENTAL_DAMAGE))
{
SetAIInteger(MAX_ELEMENTAL_DAMAGE, iDamageDone);
}
// Set the last damage done, may set to 0 of course :-P
SetAIInteger(LAST_ELEMENTAL_DAMAGE, iDamageDone);
}
// 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);
// Speak the damaged string, if applicable.
SpeakArrayString(AI_TALK_ON_DAMAGED);
// 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(iDamage > i0)
// Did they do damage to use? (IE: No DR) Some things are inapproprate
// to check if no damage was actually done.
if(nDamage > 0)
{
if(!GetSpawnInCondition(AI_FLAG_FLEEING_FEARLESS, AI_TARGETING_FLEE_MASTER))
// Speak the damaged string, if applicable.
// * Don't speak when dead. 1.4 change (an obvious one to make)
if(CanSpeak())
{
int iToDamage = GetBoundriedAIInteger(AI_DAMAGE_AT_ONCE_FOR_MORALE_PENALTY, GetMaxHitPoints()/i6, GetMaxHitPoints(), i1);
int iPenalty = GetBoundriedAIInteger(AI_DAMAGE_AT_ONCE_PENALTY, i6, i50, i1);
if(iDamage > iToDamage)
{
// 61: "[Damaged] Morale Penalty for 600 seconds [Penalty]" + IntToString(iPenalty)
DebugActionSpeakByInt(61, OBJECT_INVALID, iPenalty);
// Apply penalty
SetMoralePenalty(iPenalty, 300.0);
}
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 iHighestDamage = GetAIInteger(AI_HIGHEST_DAMAGE_AMOUNT);
// If the original was not valid...or not seen/heard, we delete it whatever.
if(!GetIsObjectValid(oHighestDamager) ||
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);
}
// Set the highest damager.
if((GetObjectSeen(oDamager) || GetObjectHeard(oDamager)) &&
iDamage >= iHighestDamage)
{
SetAIObject(AI_HIGHEST_DAMAGER, oDamager);
SetAIInteger(AI_HIGHEST_DAMAGE_AMOUNT, iDamage);
}
// Phisical damage - only sets if the last damager is the last attacker.
*/
// 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)
{
int iHighestPhisicalDamage = GetAIInteger(AI_HIGHEST_PHISICAL_DAMAGE_AMOUNT);
int iPhisicalDamage = iDamage -
// This relies upon the bug that these damage types only return damage
// from EffectDamage, and melee damage is any that remains. Wish it was
// fixed though...
GetDamageDealtByType(DAMAGE_TYPE_ACID | DAMAGE_TYPE_BLUDGEONING |
DAMAGE_TYPE_COLD | DAMAGE_TYPE_DIVINE |
DAMAGE_TYPE_ELECTRICAL | DAMAGE_TYPE_FIRE |
DAMAGE_TYPE_MAGICAL | DAMAGE_TYPE_NEGATIVE |
DAMAGE_TYPE_PIERCING | DAMAGE_TYPE_POSITIVE |
DAMAGE_TYPE_SLASHING | DAMAGE_TYPE_SONIC);
if(iHighestPhisicalDamage < iPhisicalDamage)
// Get the previous highest damage and test it
if(nPhysical > GetAIInteger(AI_HIGHEST_PHISICAL_DAMAGE_AMOUNT))
{
SetAIInteger(AI_HIGHEST_PHISICAL_DAMAGE_AMOUNT, iPhisicalDamage);
// 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);
}
}
}
@@ -155,27 +185,53 @@ void main()
{
// 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 // Else it is friendly, or invalid damager
// 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() == i1)
if(GetCurrentHitPoints() == 1)
{
// Fire the immortal damaged at 1 HP event.
FireUserEvent(AI_FLAG_UDE_DAMAGED_AT_1_HP, EVENT_DAMAGED_AT_1_HP);

View File

@@ -1,38 +1,37 @@
/************************ [On Death] *******************************************
Filename: j_ai_ondeath or nw_c2_default7
************************* [On Death] *******************************************
/*/////////////////////// [On Death] ///////////////////////////////////////////
Filename: J_AI_OnDeath or nw_c2_default7
///////////////////////// [On Death] ///////////////////////////////////////////
Speeded up no end, when compiling, with seperate Include.
Cleans up all un-droppable items, all ints and all local things when destroyed.
Check down near the bottom for a good place to add XP or corpse lines ;-)
************************* [History] ********************************************
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Added in Turn of corpses toggle
- Added in appropriate space for XP awards, marked with ideas (effect death)
************************* [Workings] *******************************************
1.4 - Removed the redudnant notes on the "You have gained 0 experience" message
///////////////////////// [Workings] ///////////////////////////////////////////
You can edit this for experience, there is a seperate section for it.
It will use DeathCheck to execute a cleanup-and-destroy script, that removes
any coprse, named "j_ai_destroyself".
************************* [Arguments] ******************************************
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: GetLastKiller.
************************* [On Death] ******************************************/
///////////////////////// [On Death] /////////////////////////////////////////*/
// We only require the constants/debug file. We have 1 function, not worth another include.
#include "j_inc_constants"
#include "J_INC_CONSTANTS"
// We need a wrapper. If the amount of deaths, got in this, is not equal to iDeaths,
// we don't execute the script, else we do. :-P
void DeathCheck(int iDeaths);
void DeathCheck(int nDeaths);
void main()
{
// If we are set to, don't fire this script at all
if(GetAIInteger(I_AM_TOTALLY_DEAD)) return;
// Pre-death-event
if(FireUserEvent(AI_FLAG_UDE_DEATH_PRE_EVENT, EVENT_DEATH_PRE_EVENT))
// We may exit if it fires
if(ExitFromUDE(EVENT_DEATH_PRE_EVENT)) return;
// Pre-death-event. Returns TRUE if we interrupt this script call.
if(FirePreUserEvent(AI_FLAG_UDE_DEATH_PRE_EVENT, EVENT_DEATH_PRE_EVENT)) return;
// Note: No AI on/off check here.
@@ -50,7 +49,7 @@ void main()
if(!GetLocalTimer(AI_TIMER_DEATH_EFFECT_DEATH))
{
// Don't apply effect death to self more then once per 2 seconds.
SetLocalTimer(AI_TIMER_DEATH_EFFECT_DEATH, f2);
SetLocalTimer(AI_TIMER_DEATH_EFFECT_DEATH, 2.0);
// This should make the last killer us.
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectResurrection(), OBJECT_SELF);
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(GetMaxHitPoints()), OBJECT_SELF);
@@ -61,50 +60,47 @@ void main()
// Set have died once, stops giving out mulitple amounts of XP.
SetAIInteger(WE_HAVE_DIED_ONCE, TRUE);
/************************ [Experience] *****************************************
THIS is the place for it, below this comment. To reward XP, you might want
to first apply EffectDeath to ourselves (uncomment the example lines) which
will remove the "You recieved 0 Experience" if you have normal XP at 0, as
the On Death event is before the reward, and therefore now our last killer
will be outselves. It will not cause any errors, oKiller is already set.
/*/////////////////////// [Experience] /////////////////////////////////////////
THIS is the place for it, below this comment.
Anything else, I leave to you. GetFirstFactionMember (and next), GiveXPToCreature,
GetXP, SetXP, GetChallengeRating all are really useful.
It is useful to use GetFirstFactionMember (and Next), GiveXPToCreature,
GetXP, SetXP, GetChallengeRating (of self) all are really useful.
Bug note: GetFirstFactionMember/Next with the PC parameter means either ONLY PC
************************* [Experience] ****************************************/
// Do XP things (Use object "oKiller").
Bug note: GetFirstFactionMember/Next with the PC parameter means either ONLY PC,
and so NPC henchmen, unless FALSE is used, will not be even recognised.
///////////////////////// [Experience] ///////////////////////////////////////*/
// Do XP things (Use object "oKiller" for who killed us).
/************************ [Experience] ****************************************/
/*/////////////////////// [Experience] ///////////////////////////////////////*/
}
// Note: Here we do a simple way of checking how many times we have died.
// Nothing special. Debugging most useful aspect.
int iDeathCounterNew = GetAIInteger(AMOUNT_OF_DEATHS);
iDeathCounterNew++;
SetAIInteger(AMOUNT_OF_DEATHS, iDeathCounterNew);
int nDeathCounterNew = GetAIInteger(AMOUNT_OF_DEATHS);
nDeathCounterNew++;
SetAIInteger(AMOUNT_OF_DEATHS, nDeathCounterNew);
// Here is the last time (in game seconds) we died. It is used in the executed script
// to make sure we don't prematurly remove areselves.
// We may want some sort of visual effect - like implosion or something, to fire.
int iDeathEffect = GetAIConstant(AI_DEATH_VISUAL_EFFECT);
int nDeathEffect = GetAIConstant(AI_DEATH_VISUAL_EFFECT);
// Valid constants from 0 and up. Apply to our location (not to us, who will go!)
if(iDeathEffect >= i0)
if(nDeathEffect >= 0)
{
ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(iDeathEffect), GetLocation(OBJECT_SELF));
ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(nDeathEffect), GetLocation(OBJECT_SELF));
}
// Default Commoner alignment changing. (If the commoner is not evil!)
if(GetLevelByClass(CLASS_TYPE_COMMONER) > i0 &&
if(GetLevelByClass(CLASS_TYPE_COMMONER) > 0 &&
GetAlignmentGoodEvil(OBJECT_SELF) != ALIGNMENT_EVIL &&
!GetSpawnInCondition(AI_FLAG_OTHER_NO_COMMONER_ALIGNMENT_CHANGE, AI_OTHER_MASTER))
{
if(GetIsPC(oKiller))
{
AdjustAlignment(oKiller, ALIGNMENT_EVIL, i5);
AdjustAlignment(oKiller, ALIGNMENT_EVIL, 5);
}
else
{
@@ -113,29 +109,31 @@ void main()
object oMaster = GetMaster(oKiller);
if(GetIsObjectValid(oMaster) && GetIsPC(oMaster))
{
AdjustAlignment(oMaster, ALIGNMENT_EVIL, i5);
AdjustAlignment(oMaster, ALIGNMENT_EVIL, 5);
}
}
}
// Always shout when we are killed. Reactions - Morale penalty, and attack the killer.
AISpeakString(I_WAS_KILLED);
// Always shout when we are killed. Reactions - Morale penalty, and
// attack the killer.
AISpeakString(AI_SHOUT_I_WAS_KILLED);
// Speaks the set death speak, like "AGGGGGGGGGGGGGGGGGGG!! NOOOO!" for instance :-)
// Note for 1.4: No need for "CanSpeak()" for this, of course.
SpeakArrayString(AI_TALK_ON_DEATH);
// First check - do we use "destroyable corpses" or not? (default, yes)
if(!GetSpawnInCondition(AI_FLAG_OTHER_TURN_OFF_CORPSES, AI_OTHER_MASTER))
{
// We will actually dissapear after 30.0 seconds if not raised.
int iTime = GetAIInteger(AI_CORPSE_DESTROY_TIME);
if(iTime == i0) // Error checking
int nTime = GetAIInteger(AI_CORPSE_DESTROY_TIME);
if(nTime == 0) // Error checking
{
iTime = i1;
nTime = 30;
}
// 64: "[Death] Checking corpse status in " + IntToString(iTime) + " [Killer] " + GetName(oKiller) + " [Times Died Now] " + IntToString(iDeathCounterNew)
DebugActionSpeakByInt(64, oKiller, iTime, IntToString(iDeathCounterNew));
// 64: "[Death] Checking corpse status in " + IntToString(nTime) + " [Killer] " + GetName(oKiller) + " [Times Died Now] " + IntToString(nDeathCounterNew)
DebugActionSpeakByInt(64, oKiller, nTime, IntToString(nDeathCounterNew));
// Delay check
DelayCommand(IntToFloat(iTime), DeathCheck(iDeathCounterNew));
DelayCommand(IntToFloat(nTime), DeathCheck(nDeathCounterNew));
}
else
{
@@ -155,10 +153,10 @@ void main()
// We need a wrapper. If the amount of deaths, got in this, is not equal to iDeaths,
// we don't execute the script, else we do. :-P
void DeathCheck(int iDeaths)
void DeathCheck(int nDeaths)
{
// Do the deaths imputted equal the amount we have suffered?
if(GetAIInteger(AMOUNT_OF_DEATHS) == iDeaths)
if(GetAIInteger(AMOUNT_OF_DEATHS) == nDeaths)
{
// - This now includes a check for Bioware's lootable functions and using them.
ExecuteScript(FILE_DEATH_CLEANUP, OBJECT_SELF);

View File

@@ -1,72 +1,83 @@
/************************ [On Disturbed] ***************************************
Filename: j_ai_ondisturbed or nw_c2_default8
************************* [On Disturbed] ***************************************
/*/////////////////////// [On Disturbed] ///////////////////////////////////////
Filename: J_AI_OnDisturbed or nw_c2_default8
///////////////////////// [On Disturbed] ///////////////////////////////////////
This will attack pickpockets, and inventory disturbers. Note: Stupidly, Bioware
made this only affect the creature by stealing. Still, oh well :-(
This means that the only event which fires it is pickpocketing.
************************* [History] ********************************************
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Changed why we determine combat round
- Any change in inventory will trigger appropriate SetWeapons again.
- Added turn of hide things.
************************* [Workings] *******************************************
1.4 - Cleaned up a bit. Removed unused declared variable.
///////////////////////// [Workings] ///////////////////////////////////////////
Only fired by stealing, great. Oh well, it will attack any disturber anyway.
It *might* not be fired if the natural spot check to notice a theft doesn't
work. No idea personally.
************************* [Arguments] ******************************************
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: GetInventoryDisturbItem, GetLastDisturbed,
GetInventoryDisturbType (I think it is always be stolen :-( ).
************************* [On Disturbed] **************************************/
///////////////////////// [On Disturbed] /////////////////////////////////////*/
#include "j_inc_other_ai"
#include "J_INC_OTHER_AI"
void main()
{
// Pre-disturbed-event
if(FireUserEvent(AI_FLAG_UDE_DISTURBED_PRE_EVENT, EVENT_DISTURBED_PRE_EVENT))
// We may exit if it fires
if(ExitFromUDE(EVENT_DISTURBED_PRE_EVENT)) return;
// Pre-disturbed-event. Returns TRUE if we interrupt this script call.
if(FirePreUserEvent(AI_FLAG_UDE_DISTURBED_PRE_EVENT, EVENT_DISTURBED_PRE_EVENT)) return;
// AI status check. Is the AI on?
if(GetAIOff()) return;
// We will set weapons if it is a weapon.
object oItem = GetInventoryDisturbItem();
int nBase = GetBaseItemType(oItem);
// Declare major variables
object oDisturber = GetLastDisturbed();
int iType = GetInventoryDisturbType();
string Middle;
object oItem = GetInventoryDisturbItem();
int nType = GetInventoryDisturbType();
int nBase = GetBaseItemType(oItem);
// We will reset weapons if it is a weapon.
// Reset weapons, or specifically healers kits.
if(GetIsObjectValid(oItem))
{
// Kits
if(nBase == BASE_ITEM_HEALERSKIT)
{
SetLocalInt(OBJECT_SELF, AI_WEAPONSETTING_SETWEAPONS, i2);
SetLocalInt(OBJECT_SELF, AI_WEAPONSETTING_SETWEAPONS, 2);
ExecuteScript(FILE_RE_SET_WEAPONS, OBJECT_SELF);
}
else // Think it is a weapon. Saves time :-)
{
SetLocalInt(OBJECT_SELF, AI_WEAPONSETTING_SETWEAPONS, i1);
SetLocalInt(OBJECT_SELF, AI_WEAPONSETTING_SETWEAPONS, 1);
ExecuteScript(FILE_RE_SET_WEAPONS, OBJECT_SELF);
}
}
// Fight! Or search!
if(GetIsObjectValid(oDisturber) && !GetIsDM(oDisturber) && !GetFactionEqual(oDisturber) &&
(iType == INVENTORY_DISTURB_TYPE_STOLEN || GetIsEnemy(oDisturber)))
if(!GetIgnoreNoFriend(oDisturber) &&
(nType == INVENTORY_DISTURB_TYPE_STOLEN || GetIsEnemy(oDisturber)))
{
// 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(oDisturber);
// Can we attack?
if(!CannotPerformCombatRound())
{
AISpeakString(CALL_TO_ARMS);
// Someone specific to attack
AISpeakString(AI_SHOUT_I_WAS_ATTACKED);
// One debug speak. We always do one.
// 65: "[Disturbed] (pickpocket) Attacking Enemy Distrube [Disturber] " + GetName(oTarget) + " [Type] " + IntToString(iType)
DebugActionSpeakByInt(65, oDisturber, iType);
DebugActionSpeakByInt(65, oDisturber, nType);
// Attack the disturber
DetermineCombatRound(oDisturber);
}
else
{
// Get allies interested.
AISpeakString(AI_SHOUT_CALL_TO_ARMS);
}
}
// Fire End-heartbeat-UDE

View File

@@ -1,6 +1,6 @@
/************************ [On Spawn] *******************************************
Filename: j_ai_onspawn or nw_c2_default9
************************* [On Spawn] *******************************************
/*/////////////////////// [On Spawn] ///////////////////////////////////////////
Filename: J_AI_OnSpawn or nw_c2_default9
///////////////////////// [On Spawn] ///////////////////////////////////////////
This file contains options that will determine some AI behaviour, and a lot
of toggles for turning things on/off. A big read, but might be worthwhile.
@@ -28,18 +28,28 @@
- Targeting is imporant :-D
- If you delete this script, there is a template for the On Spawn file
in the zip it came in, for use in the "scripttemplate" directory.
************************* [History] ********************************************
///////////////////////// [History] ////////////////////////////////////////////
Note: I have removed:
- Default "Teleporting" and exit/return (this seemed bugged anyway, or useless)
- Spawn in animation. This can be, of course, re-added.
- Day/night posting. This is uneeded, with a changed walk waypoints that does it automatically.
Changes from 1.0-1.2:
- All constants names are changed, I am afraid.
- Added Set/Delete/GetAIInteger/Constant/Object. This makes sure that the AI
doesn't ever interfere with other things - it pre-fixes all stored things
with AI_INTEGER_ (and so on)
************************* [Workings] *******************************************
1.0-1.2 - Used short amount of spawn options.
1.3 - All constants names are changed, I am afraid.
- Added Set/Delete/GetAIInteger/Constant/Object. This makes sure that the AI
doesn't ever interfere with other things - it pre-fixes all stored things
with AI_INTEGER_ (and so on)
1.4 - TO DO: Clear up some old non-working ones
- Added in User Defined part of the script, an auto-turn-off-spells for
Ranger and Paladin classes. Need to test - perhaps 1.64 fixed it?
Spawn options changed:
- Removed AI level settings (can still be done manually)
- Added optional (and off by default) fear-visual for fleeing
///////////////////////// [Workings] ///////////////////////////////////////////
Note: You can do without all the comments (it may be that you don't want
the extra KB it adds or something, although it does not at all slow down a module)
so as long as you have these at the end:
@@ -50,9 +60,9 @@
Oh, and the include file (Below, "j_inc_spawnin") must be at the top like
here. Also recommended is the AI_INTELLIGENCE and AI_MORALE being set (if
not using custom AI).
************************* [Arguments] ******************************************
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: GetIsEncounterCreature
************************* [On Spawn] ******************************************/
///////////////////////// [On Spawn] /////////////////////////////////////////*/
// Treasure Includes - See end of spawn for uncomment options.
@@ -65,7 +75,7 @@
// - This will spawn treasure based on chests placed in the module. See "x0_i0_treasure" for more information.
// This is required for all spawn in options!
#include "j_inc_spawnin"
#include "J_INC_SPAWNIN"
void main()
{
@@ -116,13 +126,13 @@ void main()
// - Remember, uncommenting one will just ignore it (so will never check target's
// AC without TARGETING_AC on)
AI_SetAITargetingValues(TARGETING_MANTALS, TARGET_LOWER, i1, i12);
AI_SetAITargetingValues(TARGETING_MANTALS, TARGET_LOWER, 1, 12);
// Spell mantals are checked only for the spell target. Either Absense of or got any.
AI_SetAITargetingValues(TARGETING_RANGE, TARGET_HIGHER, i2, i9);
AI_SetAITargetingValues(TARGETING_RANGE, TARGET_HIGHER, 2, 9);
// Range - very imporant! Basis for all ranged/spell attacks.
AI_SetAITargetingValues(TARGETING_AC, TARGET_LOWER, i2, i6);
AI_SetAITargetingValues(TARGETING_AC, TARGET_LOWER, 2, 6);
// AC is used for all phisical attacks. Lower targets lower (By default).
AI_SetAITargetingValues(TARGETING_SAVES, TARGET_LOWER, i2, i4);
AI_SetAITargetingValues(TARGETING_SAVES, TARGET_LOWER, 2, 4);
// Used for spell attacks. Saves are sorta a AC versus spells.
// Phisical protections. Used by spells, ranged and melee.
@@ -131,22 +141,22 @@ void main()
if(GetBaseAttackBonus(OBJECT_SELF) > ((GetHitDice(OBJECT_SELF)/2) + 1))
{
// Fighter/Clerics (It is over a mages BAB + 1 (IE 0.5 BAB/Level) target lower
AI_SetAITargetingValues(TARGETING_PHISICALS, TARGET_LOWER, i2, i6);
AI_SetAITargetingValues(TARGETING_PHISICALS, TARGET_LOWER, 2, 6);
}
else
{
// Mages target higher (so dispel/elemental attack those who fighters
// cannot hit as much). (the lowest BAB, under half our hit dice in BAB)
AI_SetAITargetingValues(TARGETING_PHISICALS, TARGET_HIGHER, i1, i5);
AI_SetAITargetingValues(TARGETING_PHISICALS, TARGET_HIGHER, 1, 5);
}
// Base attack bonus. Used for spells and phisical attacks. Checked with GetBaseAttackBonus.
AI_SetAITargetingValues(TARGETING_BAB, TARGET_LOWER, i1, i4);
AI_SetAITargetingValues(TARGETING_BAB, TARGET_LOWER, 1, 4);
// Hit dice - how powerful in levels the enemy is. Used for all checks.
AI_SetAITargetingValues(TARGETING_HITDICE, TARGET_LOWER, i1, i3);
AI_SetAITargetingValues(TARGETING_HITDICE, TARGET_LOWER, 1, 3);
//AI_SetAITargetingValues(TARGETING_HP_PERCENT, TARGET_LOWER, i1, i3);
//AI_SetAITargetingValues(TARGETING_HP_CURRENT, TARGET_LOWER, i1, i3);
//AI_SetAITargetingValues(TARGETING_HP_MAXIMUM, TARGET_LOWER, i1, i3);
//AI_SetAITargetingValues(TARGETING_HP_PERCENT, TARGET_LOWER, 1, 3);
//AI_SetAITargetingValues(TARGETING_HP_CURRENT, TARGET_LOWER, 1, 3);
//AI_SetAITargetingValues(TARGETING_HP_MAXIMUM, TARGET_LOWER, 1, 3);
// The HP's are the last thing to choose a target with.
/************************ [Targeting] *****************************************/
@@ -185,6 +195,9 @@ void main()
// They will flee to the nearest object of the tag below, if set.
//SetLocalString(OBJECT_SELF, AI_FLEE_OBJECT, "BOSS_TAG_OR_WHATEVER");
// This needs setting if the above is to work.
//SetSpawnInCondition(AI_FLAG_FLEEING_USE_VISUAL_EFFECT, AI_TARGETING_FLEE_MASTER);
// If this is on, we play a visual effect while we flee.
/************************ [Fleeing] *******************************************/
/************************ [Combat - Fighters] **********************************
@@ -456,15 +469,6 @@ void main()
//SetSpawnInCondition(AI_FLAG_OTHER_LAG_TARGET_NEAREST_ENEMY, AI_OTHER_MASTER);
// Ignores targeting settings. VERY good for lag/bad AI. Attacks nearest seen enemy.
/*** AI Level setting - Do not use AI_LEVEL_DEFAULT at all. ***/
//SetAIConstant(LAG_AI_LEVEL_NO_PC_OR_ENEMY_50M, AI_LEVEL_VERY_LOW);
// Changes to this AI setting if there is no enemy or PC in 50M.
//SetAIConstant(LAG_AI_LEVEL_YES_PC_OR_ENEMY_50M, AI_LEVEL_LOW);
// Changes to this AI setting if there IS an enemy or PC in 50M.
//SetAIConstant(LAG_AI_LEVEL_COMBAT, AI_LEVEL_NORMAL);
// This OVERRIDES others. Only used when a creature is put into combat.
/************************ [Other - Behaviour/Generic] *************************/
/************************ [User Defined and Shouts] ****************************
@@ -475,6 +479,10 @@ void main()
(User Defined Event = UDE)
************************* [User Defined and Shouts] ***************************/
// This is REQUIRED if we use any Pre-events. If not there, it will default
// to the default User Defined Event script for the default AI.
SetCustomUDEFileName("k_ai_onuserdef");
//SetSpawnInCondition(AI_FLAG_UDE_HEARTBEAT_EVENT, AI_UDE_MASTER); // UDE 1001
//SetSpawnInCondition(AI_FLAG_UDE_HEARTBEAT_PRE_EVENT, AI_UDE_MASTER); // UDE 1021
//SetSpawnInCondition(AI_FLAG_UDE_PERCIEVE_EVENT, AI_UDE_MASTER); // UDE 1002
@@ -549,6 +557,9 @@ void main()
// This will be said when the leader, if this creature, sends a runner.
//AI_SetSpawnInSpeakValue(AI_TALK_ON_LEADER_ATTACK_TARGET, "Help attack this target!");
// When the leader thinks target X should be attacked, it will say this.
//AI_SetSpawnInSpeakValue(AI_TALK_ON_LEADER_BOSS_SHOUT, "Come my minions! To battle!");
// This will be said when the leader, if this creature, sees an enemy and uses his "Boss Monster Shout", if turned on.
/************************ [User Defined and Shouts] ***************************/
@@ -616,10 +627,18 @@ void main()
SetBaseAttackBonus(nNumber);
}
// If we are a ranger or paladin class, do not cast spells. This can be
// manually removed if wished. To get the spells they have working correctly,
// remove this, and use Monster Abilties instead of thier normal class spells.
// if(GetLevelByClass(CLASS_TYPE_RANGER) >= 1 || GetLevelByClass(CLASS_TYPE_PALADIN) >= 1)
// {
// SetSpawnInCondition(AI_FLAG_OTHER_LAG_NO_SPELLS, AI_OTHER_MASTER);
// }
/************************ [User] **********************************************/
// Note: You shouldn't really remove this, even if they have no waypoints.
DelayCommand(f2, SpawnWalkWayPoints());
DelayCommand(2.0, SpawnWalkWayPoints());
// Delayed walk waypoints, as to not upset instant combat spawning.
// This will also check if to change to day/night posts during the walking, no heartbeats.
}

View File

@@ -1,35 +1,43 @@
/************************ [On Rested] ******************************************
Filename: j_ai_onrest or nw_c2_defaulta
************************* [On Rested] ******************************************
/*/////////////////////// [On Rested] //////////////////////////////////////////
Filename: J_AI_OnRest or nw_c2_defaulta
///////////////////////// [On Rested] //////////////////////////////////////////
This will play the sitting animation for 6 seconds, just something for resting.
Also, walks waypoints (as resting would stop this) :-) and signals event (if so be)
Feel free to edit.
It does have the spell trigger information resetting, however. This can
only be removed if they have no spell triggers, although it is hardly worth it.
************************* [History] ********************************************
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Added sitting.
************************* [Workings] *******************************************
1.4 - Will be editing this down. No need to reset anything on rest, for a
better working AI.
IDEA: Change so that we will work through all spells/feats in order.
If, at cirtain levels, we do not have any spells to cast from that
level (set in a global stored integer in the general AI) we ignore all
spells in that level. Same for each talent category (no need to use
talents for them in the spawn script).
If not in combat (EG: In heartbeat) we reset the integers saying "don't
bother checking those spells" to false.
///////////////////////// [Workings] ///////////////////////////////////////////
This fires once, at the END of resting.
If ClearAllActions is added, the resting is actually stopped, or so it seems.
It doesn't fire more then once.
************************* [Arguments] ******************************************
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: None, it seems.
************************* [On Rested] *****************************************/
///////////////////////// [On Rested] ////////////////////////////////////////*/
#include "j_inc_constants"
#include "J_INC_CONSTANTS"
// Resets all spell triggers used for sString
void LoopResetTriggers(string sString, object oTrigger);
void main()
{
// Pre-heartbeat-event
if(FireUserEvent(AI_FLAG_UDE_RESTED_PRE_EVENT, EVENT_RESTED_PRE_EVENT))
// We may exit if it fires
if(ExitFromUDE(EVENT_RESTED_PRE_EVENT)) return;
// Pre-rest-event. Returns TRUE if we interrupt this script call.
if(FirePreUserEvent(AI_FLAG_UDE_RESTED_PRE_EVENT, EVENT_RESTED_PRE_EVENT)) return;
// AI status check. Is the AI on?
if(GetAIOff()) return;
@@ -49,8 +57,8 @@ void main()
LoopResetTriggers(SPELLTRIGGER_START_OF_COMBAT, oTrigger);
}
// Some sitting for a few seconds.
ActionPlayAnimation(ANIMATION_LOOPING_SIT_CROSS, f1, f6);
DelayCommand(f9, ExecuteScript(FILE_WALK_WAYPOINTS, OBJECT_SELF));
ActionPlayAnimation(ANIMATION_LOOPING_SIT_CROSS, 1.0, 6.0);
DelayCommand(9.0, ExecuteScript(FILE_WALK_WAYPOINTS, OBJECT_SELF));
// Fire End-heartbeat-UDE
FireUserEvent(AI_FLAG_UDE_RESTED_EVENT, EVENT_RESTED_EVENT);
@@ -59,18 +67,18 @@ void main()
// Resets all spell triggers used for sString
void LoopResetTriggers(string sString, object oTrigger)
{
int iCnt, iBreak, iUsed;
for(iCnt = i1; iBreak != TRUE; iCnt++)
int nCnt, bBreak, bUsed;
for(nCnt = 1; bBreak != TRUE; nCnt++)
{
// Check max for this setting
iUsed = GetLocalInt(oTrigger, sString + USED);
if(iUsed)
bUsed = GetLocalInt(oTrigger, sString + USED);
if(bUsed)
{
DeleteLocalInt(oTrigger, sString + USED);
}
else
{
iBreak = TRUE;
bBreak = TRUE;
}
}
}

View File

@@ -1,29 +1,30 @@
/************************ [On Spell Cast At] ***********************************
/*/////////////////////// [On Spell Cast At] ///////////////////////////////////
Filename: j_ai_onspellcast or nw_c2_defaultb
************************* [On Spell Cast At] ***********************************
///////////////////////// [On Spell Cast At] ///////////////////////////////////
What does this do? Well...
- Any AOE spell effects are set in a timer, so we can react to them right
- Reacts to hostile casters, or allies in combat
And the normal attack :-)
************************* [History] ********************************************
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Added special AOE checks.
- Hide checks.
************************* [Workings] *******************************************
1.4 - Added more silent shouts. Edited the formatting. Moved a few things around.
///////////////////////// [Workings] ///////////////////////////////////////////
This is fired when EventSpellCastAt(object oCaster, int nSpell, int bHarmful=TRUE)
is signaled on the creature.
GetLastSpellCaster() = oCaster (Door, Placeable, Creature who cast it)
GetLastSpell() = nSpell (The spell cast at us)
GetLastSpellHarmful()= bHarmful (If it is harmful!)
************************* [Arguments] ******************************************
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: GetLastSpellCaster, GetLastSpellHarmful GetLastSpell()
************************* [On Spell Cast At] **********************************/
///////////////////////// [On Spell Cast At] /////////////////////////////////*/
#include "j_inc_other_ai"
#include "J_INC_OTHER_AI"
// Sets a local timer if the spell is an AOE one
void SetAOESpell(int iSpellCast, object oCaster);
void SetAOESpell(int nSpellCast, object oCaster);
// Gets the nearest AOE cast by oCaster, of sTag.
object GetNearestAOECastBy(string sTag, object oCaster);
// Gets the amount of protections we have - IE globes
@@ -31,67 +32,49 @@ int GetOurSpellLevelImmunity();
void main()
{
// Pre-heartbeat-event
if(FireUserEvent(AI_FLAG_UDE_SPELL_CAST_AT_PRE_EVENT, EVENT_SPELL_CAST_AT_PRE_EVENT))
// We may exit if it fires
if(ExitFromUDE(EVENT_SPELL_CAST_AT_PRE_EVENT)) return;
// Pre-spell cast at-event. Returns TRUE if we interrupt this script call.
if(FirePreUserEvent(AI_FLAG_UDE_SPELL_CAST_AT_PRE_EVENT, EVENT_SPELL_CAST_AT_PRE_EVENT)) return;
// AI status check. Is the AI on?
if(GetAIOff()) return;
object oCaster = GetLastSpellCaster();
int iHarmful = GetLastSpellHarmful();
int iSpellCast = GetLastSpell();
int bHarmful = GetLastSpellHarmful();
int nSpellCast = GetLastSpell();
object oAttackerOfCaster;
// If harmful, we set the spell to a timer, if an AOE one.
if(iHarmful && GetIsObjectValid(oCaster))
if(bHarmful && GetIsObjectValid(oCaster))
{
// Might set AOE spell to cast.
SetAOESpell(iSpellCast, oCaster);
SetAOESpell(nSpellCast, oCaster);
}
// If not a creature, probably an AOE or trap.
if(GetObjectType(oCaster) != OBJECT_TYPE_CREATURE)
{
// 67: "[Spell] Caster isn't a creature! May look for target [Caster] " + GetName(oCaster)
DebugActionSpeakByInt(67, oCaster);
// Shout to allies to attack, or be prepared.
AISpeakString(AI_SHOUT_CALL_TO_ARMS);
// Attack anyone else around
if(!CannotPerformCombatRound())
{
// Determine Combat Round
DetermineCombatRound();
}
}
else if(GetIsObjectValid(oCaster) && !GetIsDM(oCaster) &&
!GetIgnore(oCaster) && oCaster != OBJECT_SELF && !GetIsDead(oCaster))
// If a friend, or dead, or a DM, or invalid, or self, we ignore them.
else if(!GetIgnoreNoFriend(oCaster) && oCaster != OBJECT_SELF)
{
// 1.3 changes here:
// - We do NOT need to know if it is hostile or not, except if it is hostile
// and they are not our faction! We do, however, use iHarmful for speakstrings.
if(!GetFactionEqual(oCaster))
{
// If harmful, we attack anyone! (and if is enemy)
if(iHarmful || GetIsEnemy(oCaster))
{
if(iHarmful)
{
// Hostile spell speaksting, if set.
SpeakArrayString(AI_TALK_ON_HOSTILE_SPELL_CAST_AT);
}
// Turn of hiding check
TurnOffHiding(oCaster);
// Attack
AISpeakString(I_WAS_ATTACKED);
// We attack
if(!CannotPerformCombatRound())
{
// 68: "[Spell:Enemy/Hostile] Not in combat. Attacking: [Caster] " + GetName(oCaster)
DebugActionSpeakByInt(68, oCaster);
DetermineCombatRound(oCaster);
}
}
}
// Else, faction is equal, we normally ignore, and only move to attack.
else
// and they are not our faction! We do, however, use bHarmful for speakstrings.
// If harmful, we attack anyone! (and if is enemy)
// 1.4: Faction equal check in GetIgnoreNoFriend()
if(bHarmful || GetIsEnemy(oCaster))
{
// Spawn in condition hostile thingy
if(GetSpawnInCondition(AI_FLAG_OTHER_CHANGE_FACTIONS_TO_HOSTILE_ON_ATTACK, AI_OTHER_MASTER))
@@ -101,43 +84,74 @@ void main()
AdjustReputation(oCaster, OBJECT_SELF, -100);
}
}
// We move to the person, if they are attacking something we cannot see...
AISpeakString(CALL_TO_ARMS);
// Not in combat
if(bHarmful)
{
// * Don't speak when dead. 1.4 change (an obvious one to make)
if(CanSpeak())
{
// Hostile spell speaksting, if set.
SpeakArrayString(AI_TALK_ON_HOSTILE_SPELL_CAST_AT);
}
}
// Turn of hiding check
TurnOffHiding(oCaster);
// We attack
if(!CannotPerformCombatRound())
{
// 69: "[Spell] (ally). Not in combat. May Attack/Move [Caster] " + GetName(oCaster)
DebugActionSpeakByInt(69, oCaster);
ClearAllActions();
// Check thier target. Might not be valid (IE AOE spell at location)
oAttackerOfCaster = GetAttackTarget(oCaster);
// - Faster then DetermineCombatRound(); and looping targets until
// we get this ally, and get this attacker! :-)
if(GetIsObjectValid(oAttackerOfCaster))
// 68: "[Spell:Enemy/Hostile] Not in combat. Attacking: [Caster] " + GetName(oCaster)
DebugActionSpeakByInt(68, oCaster);
DetermineCombatRound(oCaster);
}
// Shout to allies to attack the enemy who attacked me, got via. Last Hostile Actor.
AISpeakString(AI_SHOUT_I_WAS_ATTACKED);
}
// Else, was neutral perhaps. Don't attack them anyway.
else
{
// 69: "[Spell] (ally). Not in combat. May Attack/Move [Caster] " + GetName(oCaster)
DebugActionSpeakByInt(69, oCaster);
// Set special action to investigate - as if this event was triggered
// by I_WAS_ATTACKED.
// If we are already attacking, we do not move
if(CannotPerformCombatRound())
{
// Shout to allies to attack, or be prepared.
AISpeakString(AI_SHOUT_CALL_TO_ARMS);
}
else
{
// We react as if the caster, a neutral, called for help ala
// I_WAS_ATTACKED (they might not have, might just be
// preperation for something), but normally, this is a neutral
// casting a spell. Do not respond to PC's.
if(!GetIsPC(oCaster))
{
// Move to the attack target, and wait for a proper on
// perception event (as we are not currently in combat)
ActionMoveToObject(oCaster, TRUE);
}
else
{
// Move to the caster otherwise
ActionMoveToObject(oCaster, TRUE);
IWasAttackedResponse(oCaster);
}
}
}
}
// If they are not a faction equal, and valid, we help them.
else if(GetIsObjectValid(oCaster) && GetFactionEqual(oCaster))
{
IWasAttackedResponse(oCaster);
}
// Fire End-spell cast at-UDE
FireUserEvent(AI_FLAG_UDE_SPELL_CAST_AT_EVENT, EVENT_SPELL_CAST_AT_EVENT);
}
// Sets a local timer if the spell is an AOE one
void SetAOESpell(int iSpellCast, object oCaster)
void SetAOESpell(int nSpellCast, object oCaster)
{
// Check it is one we can check
int iImmuneLevel = GetOurSpellLevelImmunity();
switch(iSpellCast)
int bStop = TRUE;
switch(nSpellCast)
{
case SPELL_ACID_FOG:
case SPELL_MIND_FOG:
@@ -159,81 +173,82 @@ void SetAOESpell(int iSpellCast, object oCaster)
case SPELL_VINE_MINE_HAMPER_MOVEMENT:
case SPELL_VINE_MINE_ENTANGLE:
{
// Pass though, unless we are totally immune.
if(iImmuneLevel >= i9)
{
return;
}
}
break;
// Any other spell
default:
{
return;
bStop = FALSE;
}
break;
}
// Check immune level
int nImmuneLevel = GetOurSpellLevelImmunity();
if(nImmuneLevel >= 9)
{
bStop = TRUE;
}
// Check
if(bStop == TRUE)
{
return;
}
// We do use intelligence here...
int iAIInt = GetBoundriedAIInteger(AI_INTELLIGENCE);
int iIgnoreSaves;
int iIgnoreImmunities;
int nAIInt = GetBoundriedAIInteger(AI_INTELLIGENCE);
int bIgnoreSaves;
int bIgnoreImmunities;
object oAOE;
// If it is low, we ignore all things that we could ignore with it...
if(iAIInt <= i3)
if(nAIInt <= 3)
{
iIgnoreSaves = TRUE;
iIgnoreImmunities = TRUE;
bIgnoreSaves = TRUE;
bIgnoreImmunities = TRUE;
}
// Average ignores saves
else if(iAIInt <= i7)
else if(nAIInt <= 7)
{
iIgnoreSaves = TRUE;
iIgnoreImmunities = FALSE;
bIgnoreSaves = TRUE;
bIgnoreImmunities = FALSE;
}
// Else, we do both.
else
{
iIgnoreSaves = FALSE;
iIgnoreImmunities = FALSE;
bIgnoreSaves = FALSE;
bIgnoreImmunities = FALSE;
}
int SetAOE = FALSE;// TRUE means set to timer
int iSaveDC = i11;
int bSetAOE = FALSE;// TRUE means set to timer
int nSaveDC = 11;
// Get the caster DC, the most out of WIS, INT or CHA...
int iInt = GetAbilityModifier(ABILITY_INTELLIGENCE, oCaster);
int iWis = GetAbilityModifier(ABILITY_WISDOM, oCaster);
int iCha = GetAbilityModifier(ABILITY_CHARISMA, oCaster);
int nInt = GetAbilityModifier(ABILITY_INTELLIGENCE, oCaster);
int nWis = GetAbilityModifier(ABILITY_WISDOM, oCaster);
int nCha = GetAbilityModifier(ABILITY_CHARISMA, oCaster);
if(iInt > iWis && iInt > iCha)
if(nInt > nWis && nInt > nCha)
{
iSaveDC += iInt;
nSaveDC += nInt;
}
else if(iWis > iCha)
else if(nWis > nCha)
{
iSaveDC += iWis;
nSaveDC += nWis;
}
else
{
iSaveDC += iCha;
nSaveDC += nCha;
}
// Note:
// - No reaction type/friendly checks. Signal Event is only fired if the
// spell WILL pierce any PvP/Friendly/Area settings
// We check immunities here, please note...
switch(iSpellCast)
switch(nSpellCast)
{
// First: IS GetIsReactionTypeHostile ones.
case SPELL_EVARDS_BLACK_TENTACLES:
// Fortitude save, but if we are immune to the hits, its impossible to hurt us
{
// If save immune OR AC immune, we ignore this.
if(i25 >= GetAC(OBJECT_SELF) && iImmuneLevel < i4 &&
((GetFortitudeSavingThrow(OBJECT_SELF) < iSaveDC + i2) || iIgnoreSaves))
if(25 >= GetAC(OBJECT_SELF) && nImmuneLevel < 4 &&
((GetFortitudeSavingThrow(OBJECT_SELF) < nSaveDC + 2) || bIgnoreSaves))
{
SetAOE = TRUE;
bSetAOE = TRUE;
// Nearest string of tag
oAOE = GetNearestAOECastBy(AI_AOE_PER_EVARDS_BLACK_TENTACLES, oCaster);
}
@@ -243,11 +258,11 @@ void SetAOESpell(int iSpellCast, object oCaster)
// d4 damage. LOTS of speed loss.
// Reflex save, or immunity, would stop the speed
{
if(iImmuneLevel < i3 &&
(!GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_MOVEMENT_SPEED_DECREASE) || iIgnoreImmunities) &&
((GetReflexSavingThrow(OBJECT_SELF) < iSaveDC + i5) || iIgnoreSaves))
if(nImmuneLevel < 3 &&
(!GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_MOVEMENT_SPEED_DECREASE) || bIgnoreImmunities) &&
((GetReflexSavingThrow(OBJECT_SELF) < nSaveDC + 5) || bIgnoreSaves))
{
SetAOE = TRUE;
bSetAOE = TRUE;
// Both use ENTANGLE AOE's
oAOE = GetNearestAOECastBy(AI_AOE_PER_ENTANGLE, oCaster);
}
@@ -256,12 +271,12 @@ void SetAOESpell(int iSpellCast, object oCaster)
case SPELL_ENTANGLE:
case SPELL_VINE_MINE_ENTANGLE:
{
if(iImmuneLevel < i1 &&
(!GetHasFeat(FEAT_WOODLAND_STRIDE) || iIgnoreImmunities) &&
(!GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_ENTANGLE) || iIgnoreImmunities) &&
((GetReflexSavingThrow(OBJECT_SELF) < iSaveDC + i4) || iIgnoreSaves))
if(nImmuneLevel < 1 &&
(!GetHasFeat(FEAT_WOODLAND_STRIDE) || bIgnoreImmunities) &&
(!GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_ENTANGLE) || bIgnoreImmunities) &&
((GetReflexSavingThrow(OBJECT_SELF) < nSaveDC + 4) || bIgnoreSaves))
{
SetAOE = TRUE;
bSetAOE = TRUE;
// Both use ENTANGLE AOE's
oAOE = GetNearestAOECastBy(AI_AOE_PER_ENTANGLE, oCaster);
}
@@ -269,12 +284,12 @@ void SetAOESpell(int iSpellCast, object oCaster)
break;
case SPELL_WEB:
{
if(iImmuneLevel < i1 &&
(!GetHasFeat(FEAT_WOODLAND_STRIDE) || iIgnoreImmunities) &&
(!GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_ENTANGLE) || iIgnoreImmunities) &&
((GetReflexSavingThrow(OBJECT_SELF) < iSaveDC + i4) || iIgnoreSaves))
if(nImmuneLevel < 1 &&
(!GetHasFeat(FEAT_WOODLAND_STRIDE) || bIgnoreImmunities) &&
(!GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_ENTANGLE) || bIgnoreImmunities) &&
((GetReflexSavingThrow(OBJECT_SELF) < nSaveDC + 4) || bIgnoreSaves))
{
SetAOE = TRUE;
bSetAOE = TRUE;
// Web AOE
oAOE = GetNearestAOECastBy(AI_AOE_PER_WEB, oCaster);
}
@@ -283,12 +298,12 @@ void SetAOESpell(int iSpellCast, object oCaster)
// Fort save
case SPELL_STINKING_CLOUD:
{
if(iImmuneLevel < i3 &&
(!GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_POISON) || iIgnoreImmunities) &&
(!GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_DAZED) || iIgnoreImmunities) &&
((GetFortitudeSavingThrow(OBJECT_SELF) < iSaveDC + i6) || iIgnoreSaves))
if(nImmuneLevel < 3 &&
(!GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_POISON) || bIgnoreImmunities) &&
(!GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_DAZED) || bIgnoreImmunities) &&
((GetFortitudeSavingThrow(OBJECT_SELF) < nSaveDC + 6) || bIgnoreSaves))
{
SetAOE = TRUE;
bSetAOE = TRUE;
// Stinking cloud persistant AOE.
oAOE = GetNearestAOECastBy(AI_AOE_PER_FOGSTINK, oCaster);
}
@@ -297,10 +312,10 @@ void SetAOESpell(int iSpellCast, object oCaster)
// Fort save
case SPELL_CLOUD_OF_BEWILDERMENT:
{
if(iImmuneLevel < i2 &&
((GetFortitudeSavingThrow(OBJECT_SELF) < iSaveDC + i7) || iIgnoreSaves))
if(nImmuneLevel < 2 &&
((GetFortitudeSavingThrow(OBJECT_SELF) < nSaveDC + 7) || bIgnoreSaves))
{
SetAOE = TRUE;
bSetAOE = TRUE;
// Bewilderment cloud persistant AOE.
oAOE = GetNearestAOECastBy(AI_AOE_PER_FOGBEWILDERMENT, oCaster);
}
@@ -309,11 +324,11 @@ void SetAOESpell(int iSpellCast, object oCaster)
// Special: Mind save is the effect.
case SPELL_STONEHOLD:
{
if(iImmuneLevel < i6 &&
(!GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_MIND_SPELLS) || iIgnoreImmunities) &&
((GetWillSavingThrow(OBJECT_SELF) < iSaveDC + i7) || iIgnoreSaves))
if(nImmuneLevel < 6 &&
(!GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_MIND_SPELLS) || bIgnoreImmunities) &&
((GetWillSavingThrow(OBJECT_SELF) < nSaveDC + 7) || bIgnoreSaves))
{
SetAOE = TRUE;
bSetAOE = TRUE;
// Stonehold persistant AOE.
oAOE = GetNearestAOECastBy(AI_AOE_PER_STONEHOLD, oCaster);
}
@@ -322,11 +337,11 @@ void SetAOESpell(int iSpellCast, object oCaster)
// Special: EFFECT_TYPE_SAVING_THROW_DECREASE is the effect.
case SPELL_MIND_FOG:
{
if(iImmuneLevel < i5 &&
(!GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_SAVING_THROW_DECREASE) || iIgnoreImmunities) &&
((GetWillSavingThrow(OBJECT_SELF) < iSaveDC + i6) || iIgnoreSaves))
if(nImmuneLevel < 5 &&
(!GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_SAVING_THROW_DECREASE) || bIgnoreImmunities) &&
((GetWillSavingThrow(OBJECT_SELF) < nSaveDC + 6) || bIgnoreSaves))
{
SetAOE = TRUE;
bSetAOE = TRUE;
// Mind fog
oAOE = GetNearestAOECastBy(AI_AOE_PER_FOGMIND, oCaster);
}
@@ -335,12 +350,12 @@ void SetAOESpell(int iSpellCast, object oCaster)
// Special: Feats, knockdown
case SPELL_GREASE:
{
if(iImmuneLevel < i1 &&
(!GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_KNOCKDOWN) || iIgnoreImmunities) &&
(!GetHasFeat(FEAT_WOODLAND_STRIDE, OBJECT_SELF) || iIgnoreImmunities) &&
((GetReflexSavingThrow(OBJECT_SELF) < iSaveDC + i2) || iIgnoreSaves))
if(nImmuneLevel < 1 &&
(!GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_KNOCKDOWN) || bIgnoreImmunities) &&
(!GetHasFeat(FEAT_WOODLAND_STRIDE, OBJECT_SELF) || bIgnoreImmunities) &&
((GetReflexSavingThrow(OBJECT_SELF) < nSaveDC + 2) || bIgnoreSaves))
{
SetAOE = TRUE;
bSetAOE = TRUE;
// Grease
oAOE = GetNearestAOECastBy(AI_AOE_PER_GREASE, oCaster);
}
@@ -351,23 +366,23 @@ void SetAOESpell(int iSpellCast, object oCaster)
case SPELL_INCENDIARY_CLOUD:// reflex
case SPELL_WALL_OF_FIRE:// Reflex
{
if(iImmuneLevel < i6 &&
(((GetReflexSavingThrow(OBJECT_SELF) < iSaveDC + i6) &&
if(nImmuneLevel < 6 &&
(((GetReflexSavingThrow(OBJECT_SELF) < nSaveDC + 6) &&
!GetHasFeat(FEAT_IMPROVED_EVASION) &&
!GetHasFeat(FEAT_EVASION)) || iIgnoreSaves))
!GetHasFeat(FEAT_EVASION)) || bIgnoreSaves))
{
SetAOE = TRUE;
if(iSpellCast == SPELL_BLADE_BARRIER)
bSetAOE = TRUE;
if(nSpellCast == SPELL_BLADE_BARRIER)
{
// BB
oAOE = GetNearestAOECastBy(AI_AOE_PER_WALLBLADE, oCaster);
}
else if(iSpellCast == SPELL_INCENDIARY_CLOUD)
else if(nSpellCast == SPELL_INCENDIARY_CLOUD)
{
// Fog of fire
oAOE = GetNearestAOECastBy(AI_AOE_PER_FOGFIRE, oCaster);
}
else if(iSpellCast == SPELL_WALL_OF_FIRE)
else if(nSpellCast == SPELL_WALL_OF_FIRE)
{
// Wall of fire
oAOE = GetNearestAOECastBy(AI_AOE_PER_WALLFIRE, oCaster);
@@ -379,20 +394,20 @@ void SetAOESpell(int iSpellCast, object oCaster)
case SPELL_CLOUDKILL:// No save!
case SPELL_CREEPING_DOOM: // No save!
{
if(iImmuneLevel < i6)
if(nImmuneLevel < 6)
{
SetAOE = TRUE;
if(iSpellCast == SPELL_ACID_FOG)
bSetAOE = TRUE;
if(nSpellCast == SPELL_ACID_FOG)
{
// Acid fog
oAOE = GetNearestAOECastBy(AI_AOE_PER_FOGACID, oCaster);
}
else if(iSpellCast == SPELL_CLOUDKILL)
else if(nSpellCast == SPELL_CLOUDKILL)
{
// Cloud Kill
oAOE = GetNearestAOECastBy(AI_AOE_PER_FOGKILL, oCaster);
}
else if(iSpellCast == SPELL_CREEPING_DOOM)
else if(nSpellCast == SPELL_CREEPING_DOOM)
{
// Creeping doom
oAOE = GetNearestAOECastBy(AI_AOE_PER_CREEPING_DOOM, oCaster);
@@ -402,28 +417,28 @@ void SetAOESpell(int iSpellCast, object oCaster)
// Storm - because the AI likes it, we stay in it if it is ours :-)
case SPELL_STORM_OF_VENGEANCE: // Reflex partial. No check, always damages.
{
if(oCaster != OBJECT_SELF && iImmuneLevel < i9)
if(oCaster != OBJECT_SELF && nImmuneLevel < 9)
{
SetAOE = TRUE;
bSetAOE = TRUE;
// Storm of vengance
oAOE = GetNearestAOECastBy(AI_AOE_PER_STORM, oCaster);
}
}
}
if(SetAOE)
if(bSetAOE)
{
if(!GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(iSpellCast)))
if(!GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(nSpellCast)))
{
SetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(iSpellCast), f18);
SetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(nSpellCast), 18.0);
// Set nearest AOE
if(GetIsObjectValid(oAOE))
{
// Set nearest AOE of this spell to the local
object oNearest = GetAIObject(AI_TIMER_AOE_SPELL_EVENT + IntToString(iSpellCast));
object oNearest = GetAIObject(AI_TIMER_AOE_SPELL_EVENT + IntToString(nSpellCast));
if(GetDistanceToObject(oAOE) < GetDistanceToObject(oNearest) ||
!GetIsObjectValid(oNearest))
{
SetAIObject(AI_TIMER_AOE_SPELL_EVENT + IntToString(iSpellCast), oAOE);
SetAIObject(AI_TIMER_AOE_SPELL_EVENT + IntToString(nSpellCast), oAOE);
}
}
}
@@ -432,8 +447,8 @@ void SetAOESpell(int iSpellCast, object oCaster)
// Gets the nearest AOE cast by oCaster, of sTag.
object GetNearestAOECastBy(string sTag, object oCaster)
{
int iCnt = i1;
object oAOE = GetNearestObjectByTag(sTag, OBJECT_SELF, iCnt);
int nCnt = 1;
object oAOE = GetNearestObjectByTag(sTag, OBJECT_SELF, nCnt);
object oReturn = OBJECT_INVALID;
// Loop
while(GetIsObjectValid(oAOE) && !GetIsObjectValid(oReturn))
@@ -443,8 +458,8 @@ object GetNearestAOECastBy(string sTag, object oCaster)
{
oReturn = oAOE;
}
iCnt++;
oAOE = GetNearestObjectByTag(sTag, OBJECT_SELF, iCnt);
nCnt++;
oAOE = GetNearestObjectByTag(sTag, OBJECT_SELF, nCnt);
}
return oReturn;
}
@@ -452,27 +467,27 @@ object GetNearestAOECastBy(string sTag, object oCaster)
// Gets the amount of protections we have - IE globes
int GetOurSpellLevelImmunity()
{
int iNatural = GetLocalInt(OBJECT_SELF, AI_SPELL_IMMUNE_LEVEL);
int nNatural = GetLocalInt(OBJECT_SELF, AI_SPELL_IMMUNE_LEVEL);
// Stop here, if natural is over 4
if(iNatural > i4) return iNatural;
if(nNatural > 4) return nNatural;
// Big globe affects 4 or lower spells
if(GetHasSpellEffect(SPELL_GLOBE_OF_INVULNERABILITY, OBJECT_SELF) || iNatural >= i4)
return i4;
if(GetHasSpellEffect(SPELL_GLOBE_OF_INVULNERABILITY, OBJECT_SELF) || nNatural >= 4)
return 4;
// Minor globe is 3 or under
if(GetHasSpellEffect(SPELL_MINOR_GLOBE_OF_INVULNERABILITY, OBJECT_SELF) ||
// Shadow con version
GetHasSpellEffect(SPELL_GREATER_SHADOW_CONJURATION_MINOR_GLOBE, OBJECT_SELF) ||
iNatural >= i3)
return i3;
nNatural >= 3)
return 3;
// 2 and under is ethereal visage.
if(GetHasSpellEffect(SPELL_ETHEREAL_VISAGE, OBJECT_SELF) || iNatural >= i2)
return i2;
if(GetHasSpellEffect(SPELL_ETHEREAL_VISAGE, OBJECT_SELF) || nNatural >= 2)
return 2;
// Ghostly Visarge affects 1 or 0 level spells, and any spell immunity.
if(GetHasSpellEffect(SPELL_GHOSTLY_VISAGE, OBJECT_SELF) || iNatural >= i1 ||
if(GetHasSpellEffect(SPELL_GHOSTLY_VISAGE, OBJECT_SELF) || nNatural >= 1 ||
// Or shadow con version.
GetHasSpellEffect(SPELL_GREATER_SHADOW_CONJURATION_MIRROR_IMAGE, OBJECT_SELF))
return i1;
// Return iNatural, which is 0-9
return 1;
// Return nNatural, which is 0-9
return FALSE;
}

View File

@@ -1,8 +1,8 @@
/************************ [On User Defined] ************************************
Filename: j_ai_onuserdef or nw_c2_defaultd
************************* [Workings] *******************************************
This is NOT needed, but I have, because of new Pre-events, added it to the
files for completeness and documentation.
/*/////////////////////// [On User Defined] ////////////////////////////////////
Filename: J_AI_OnUserDef or nw_c2_defaultd
///////////////////////// [Workings] ///////////////////////////////////////////
1.4 Adds proper Pre-event functionality. Use this to make sure you keep the
AI working, but making small additions using this event.
Can be deleted to save space if you want :-)
@@ -47,23 +47,29 @@
You can delete sections you don't need, and is recommended as long as you know
how to use User Defined events.
************************* [History] ********************************************
///////////////////////// [History] ////////////////////////////////////////////
1.3 - Added in with some documentation
************************* [Arguments] ******************************************
1.4 - Changed Pre-events. Now, it uses Execute Script. Will need to set
a special string on the creature to now what script to fire.
- It means they work correctly, however!
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: Dependant on event. See seperate event scripts.
************************* [On User Defined] ***********************************/
///////////////////////// [On User Defined] //////////////////////////////////*/
// This contains a lot of useful things.
// - Combat starting
// - Constant values
// - Get/Set spawn in values.
#include "j_inc_other_ai"
#include "J_INC_OTHER_AI"
// This contains some useful things to get NPC's to attack and so on.
#include "j_inc_npc_attack"
#include "J_INC_NPC_ATTACK"
/************************ [UDE Values] *****************************************
These are uneeded, but here for reference. Use the constants in the file
"j_inc_constants" like "EVENT_HEARTBEAT_PRE_EVENT" which is classed as 1021.
* The normal death event might not fire before the creature has vanished.
Use the Pre-event (but with no stopping the death event) if you want a special
death event to happen.
Name Normal-End event - Pre-Event
Heartbeat Event 1001 1021
@@ -84,9 +90,11 @@
void main()
{
// Get the user defined number.
int iEvent = GetUserDefinedEventNumber();
// * NOTE: YOU MUST USE AI_GetUDENumber(), not GetUserDefinedEventNumber()!
int nEvent = AI_GetUDENumber();
// Events.
switch(iEvent)
switch(nEvent)
{
// Event Heartbeat
// Arguments: Basically, none. Nothing activates this script. Fires every 6 seconds.

View File

@@ -1,6 +1,6 @@
/************************ [On Blocked] *****************************************
Filename: j_ai_onblocked or nw_c2_defaulte
************************* [On Blocked] *****************************************
/*/////////////////////// [On Blocked] /////////////////////////////////////////
Filename: J_AI_OnBlocked or nw_c2_defaulte
///////////////////////// [On Blocked] /////////////////////////////////////////
Added in user defined constant - won't open any doors.
0 = Default (even if not set) opens as appropriate
1 = Always bashes the door.
@@ -16,14 +16,20 @@
Note: This also fires for blocking via. creatures. It is optimised, and
works by re-targeting and doing a few small things to do with blocking.
************************* [History] ********************************************
///////////////////////// [History] ////////////////////////////////////////////
1.0 - Opens with Knock. Unlocks door. Ignores trapped doors.
1.3 - Debug messages.
- New events, even if the change of using them is small!
- No ClearAllactions so any previous movings will carry on once the door is gone.
- Removed debug messages
1.3 - Added Creature reaction code
************************* [Workings] *******************************************
- Added Creature reaction code
1.4 - Need to add a "hands" check (done on spawn, to set a setting to not
open doors at all, IE: We do NOT have hands, do not open doors), so
its a little more realistic "out of the box"
- Fixed an instance of GetObjectSeen being repeated.
- Fixed the variable AI_DOOR_INTELLIGENCE not being got via GetAIInteger().
- Removed unneeded else statement.
///////////////////////// [Workings] ///////////////////////////////////////////
Uses simple code to deal with a door in the best way possible.
Uses DoDoorAction, which is added to the top of an action queue and doesn't,
@@ -31,10 +37,10 @@
is like this)
Creatures are reacted by with ClearAllActions usually.
************************* [Arguments] ******************************************
///////////////////////// [Arguments] //////////////////////////////////////////
Arguments: GetBlockingDoor, GetIsDoorActionPossible, GetLocked, GetLockKeyRequired
GetLockKeyTag, GetLockUnlockDC, GetPlotFlag, DoDoorAction
************************* [On Blocked] ****************************************/
///////////////////////// [On Blocked] ///////////////////////////////////////*/
#include "J_INC_OTHER_AI"
@@ -45,10 +51,8 @@ int RangedAttack(object oTarget = OBJECT_INVALID);
void main()
{
// Pre-blocked-event
if(FireUserEvent(AI_FLAG_UDE_ON_BLOCKED_PRE_EVENT, EVENT_ON_BLOCKED_PRE_EVENT))
// We may exit if it fires
if(ExitFromUDE(EVENT_ON_BLOCKED_PRE_EVENT)) return;
// Pre-on blocked-event. Returns TRUE if we interrupt this script call.
if(FirePreUserEvent(AI_FLAG_UDE_ON_BLOCKED_PRE_EVENT, EVENT_ON_BLOCKED_PRE_EVENT)) return;
// AI status check. Is the AI on?
if(GetAIOff()) return;
@@ -74,19 +78,20 @@ void main()
if(GetLocalTimer(AI_TIMER_BLOCKED)) return;
// Set the timer for 1 second
SetLocalTimer(AI_TIMER_BLOCKED, f1);
SetLocalTimer(AI_TIMER_BLOCKED, 1.0);
// Is it an enemy?
if(GetIsEnemy(oBlocker))
{
// Check if seen or heard
if(GetObjectSeen(oBlocker) || GetObjectSeen(oBlocker))
if(GetObjectSeen(oBlocker) || GetObjectHeard(oBlocker))
{
// Enemy :-) We can re-target (as know of thier presence), using
// them as a target.
// - This overrides even casting a spell - basically, as we should
// be moving, this will re-cast it at someone or something in range
SetAIObject(AI_ATTACK_SPECIFIC_OBJECT, oBlocker);
// Check if we can do combat - if we cannot, we can re-do combat
// next time
if(!GetIsBusyWithAction())
@@ -106,6 +111,9 @@ void main()
{
SetAIObject(AI_LAST_TO_GO_INVISIBLE, oBlocker);
}
// Shout to allies
AISpeakString(AI_SHOUT_CALL_TO_ARMS);
// Check if we can do combat
if(!GetIsBusyWithAction())
{
@@ -125,42 +133,62 @@ void main()
// Were we attacking in combat?
object oPrevious = GetAttackTarget();
// Check action
if(GetCurrentAction() == ACTION_ATTACKOBJECT)
{
// Action attack, normally means melee attack. If we can, we
// attack our previous target if seen, ELSE we will re-initate
// combat.
AISpeakString(I_WAS_ATTACKED);
// This gets set to FALSE if we can cutthrough attack,
// or whatever.
int bPreviousAttackFailed = FALSE;
// Check if we can see our previous target
if(GetObjectSeen(oPrevious) ||
(GetObjectHeard(oPrevious) && LineOfSightObject(OBJECT_SELF, oPrevious)))
{
// We can! see if we can re-attack with ranged weapon, else
// doesn't matter we can see them
if(RangedAttack(oPrevious)) return;
bPreviousAttackFailed = RangedAttack(oPrevious);
}
else
// We have not stopped the script - so determine combat
// round against nearest seen or heard enemy!
if(RangedAttack()) return;
// Else normal round to try and get a new target
ClearAllActions();
DetermineCombatRound();
// If we havn't added an action yet...
if(bPreviousAttackFailed == FALSE)
{
// We have not stopped the script - so determine combat
// round against nearest seen or heard enemy!
if(!RangedAttack())
{
// Else normal round to try and get a new target
ClearAllActions();
DetermineCombatRound();
}
}
// Action attack, normally means melee attack. If we can, we
// attack our previous target if seen, ELSE we will re-initate
// combat.
AISpeakString(AI_SHOUT_I_WAS_ATTACKED);
// Fire the On blocked event as normal
FireBlockedEvent();
return;
}
else // if(iAction == ACTION_CASTSPELL and others)
else // if(nAction == ACTION_CASTSPELL and others)
{
// Reinitate combat
// Reinitate combat, but don't attack oPrevious
ClearAllActions();
DetermineCombatRound();
// Action attack, normally means melee attack. If we can, we
// attack our previous target if seen, ELSE we will re-initate
// combat.
AISpeakString(AI_SHOUT_I_WAS_ATTACKED);
// Fire the On blocked event as normal
FireBlockedEvent();
return;
}
}
}
// Placeable - Not sure it can be returned, however, we can add it to the
// type if/else check.
// Placeable - Currently not returned, however, added just in case!
else if(nBlockerType == OBJECT_TYPE_PLACEABLE)
{
// Check for plot, and therefore attack it to bring it down.
@@ -171,26 +199,29 @@ void main()
{
// Do placeable action
DoPlaceableObjectAction(oBlocker, PLACEABLE_ACTION_BASH);
FireBlockedEvent();
return;
}
return;
}
// Door behaviour
else if(nBlockerType == OBJECT_TYPE_DOOR)
{
int iDoorIntelligence = GetLocalInt(OBJECT_SELF, AI_DOOR_INTELLIGENCE);
int iInt = GetAbilityScore(OBJECT_SELF, ABILITY_INTELLIGENCE);
if(iDoorIntelligence == i1)// 1 = Always bashes the doors, plot, locked or anything.
int nDoorIntelligence = GetAIInteger(AI_DOOR_INTELLIGENCE);
int nInt = GetAbilityScore(OBJECT_SELF, ABILITY_INTELLIGENCE);
if(nDoorIntelligence == 1)// 1 = Always bashes the doors, plot, locked or anything.
{
DoDoorAction(oBlocker, DOOR_ACTION_BASH);
// We re-initiate combat.
FireBlockedEvent();
return;
}
else if(iDoorIntelligence == i2)// 2 = Never open anything, bashing or not.
else if(nDoorIntelligence == 2)// 2 = Never open anything, bashing or not.
{
FireBlockedEvent();
return;
}
else if(iDoorIntelligence == i3)// 3 = Never tries anything against plot doors.
else if(nDoorIntelligence == 3)// 3 = Never tries anything against plot doors.
{
if(GetPlotFlag(oBlocker))
{
@@ -198,10 +229,10 @@ void main()
return;
}
}
if(iInt >= i5)
if(nInt >= 5)
{
// Need some intelligence :-)
if(iInt >= i7)
if(nInt >= 7)
{
// Right, first, we may...shock...open it!!!
// Checks Key, lock, trap and if the action is possible.
@@ -221,17 +252,17 @@ void main()
!GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_OPENING_LOCKED_DOORS, AI_OTHER_COMBAT_MASTER) &&
!GetLockKeyRequired(oBlocker) && GetHasSkill(SKILL_OPEN_LOCK) &&
GetIsDoorActionPossible(oBlocker, DOOR_ACTION_UNLOCK) && !GetIsTrapped(oBlocker) &&
(GetSkillRank(SKILL_OPEN_LOCK) >= (GetLockLockDC(oBlocker) - i20)))
(GetSkillRank(SKILL_OPEN_LOCK) >= (GetLockLockDC(oBlocker) - 20)))
{
DoDoorAction(oBlocker, DOOR_ACTION_UNLOCK);
FireBlockedEvent();
return;
}
// Specilist thing - knock
if(iInt >= i10)
if(nInt >= 10)
{
if((GetIsDoorActionPossible(oBlocker, DOOR_ACTION_KNOCK)) &&
GetLockUnlockDC(oBlocker) <= i25 &&
GetLockUnlockDC(oBlocker) <= 25 &&
!GetLockKeyRequired(oBlocker) && GetHasSpell(SPELL_KNOCK))
{
DoDoorAction(oBlocker, DOOR_ACTION_KNOCK);
@@ -239,16 +270,16 @@ void main()
return;
}
}
// If Our Int is over 5, we will bash after everything else.
if(GetIsDoorActionPossible(oBlocker, DOOR_ACTION_BASH) && !GetPlotFlag(oBlocker))
}
// If Our Int is over 5, we will bash after everything else.
if(GetIsDoorActionPossible(oBlocker, DOOR_ACTION_BASH) && !GetPlotFlag(oBlocker))
{
if(GetAttackTarget() != oBlocker)
{
if(GetAttackTarget() != oBlocker)
{
DoDoorAction(oBlocker, DOOR_ACTION_BASH);
}
FireBlockedEvent();
return;
DoDoorAction(oBlocker, DOOR_ACTION_BASH);
}
FireBlockedEvent();
return;
}
}
}
@@ -284,11 +315,13 @@ int RangedAttack(object oTarget)
// Ranged weapon attack against oTarget
// doesn't matter we can see them
object oRanged = GetAIObject(AI_WEAPON_RANGED);
int iAmmo = GetAIInteger(AI_WEAPON_RANGED_AMMOSLOT);
int nAmmo = GetAIInteger(AI_WEAPON_RANGED_AMMOSLOT);
// Check ammo and validness
if(GetIsObjectValid(oRanged) && (iAmmo == INVENTORY_SLOT_RIGHTHAND ||
GetIsObjectValid(GetItemInSlot(iAmmo))))
if(GetIsObjectValid(oRanged) && (nAmmo == INVENTORY_SLOT_RIGHTHAND ||
GetIsObjectValid(GetItemInSlot(nAmmo))))
{
// Attack with it
ClearAllActions();
ActionEquipItem(oRanged, INVENTORY_SLOT_RIGHTHAND);
ActionAttack(oRangedTarget);

View File

@@ -29,7 +29,7 @@ void main()
// These MUST be called! the AI might fail to work correctly if they don't fire!
// Note: You shouldn't really remove this. Also performs hiding ETC.
DelayCommand(f2, SpawnWalkWayPoints());
DelayCommand(2.0f, SpawnWalkWayPoints());
// Delayed walk waypoints, as to not upset instant combat spawning.
// This will also check if to change to day/night posts during the walking, no heartbeats.
}

View File

@@ -32,7 +32,7 @@ void main()
}
// Note: You shouldn't really remove this. Also performs hiding ETC.
DelayCommand(f2, SpawnWalkWayPoints());
DelayCommand(2.0f, SpawnWalkWayPoints());
// Delayed walk waypoints, as to not upset instant combat spawning.
// This will also check if to change to day/night posts during the walking, no heartbeats.
}

View File

@@ -31,7 +31,7 @@ void main()
// These MUST be called! the AI might fail to work correctly if they don't fire!
// Note: You shouldn't really remove this. Also performs hiding ETC.
DelayCommand(f2, SpawnWalkWayPoints());
DelayCommand(2.0f, SpawnWalkWayPoints());
// Delayed walk waypoints, as to not upset instant combat spawning.
// This will also check if to change to day/night posts during the walking, no heartbeats.
}

View File

@@ -19,7 +19,7 @@ void main()
int nUser = GetUserDefinedEventNumber();
int nChange = GetLocalInt(OBJECT_SELF,"NW_LYCANTHROPE");
effect eShape = EffectPolymorph(POLYMORPH_TYPE_RAKSHASA); //Use one of the polymorph constants here (WERE_RAT, WERE_WOLF or WERE_CAT)
effect eShape = EffectPolymorph(88); //Use one of the polymorph constants here (WERE_RAT, WERE_WOLF or WERE_CAT)
effect eVis = EffectVisualEffect(VFX_FNF_SUMMON_UNDEAD);
if(nUser == 1005 && nChange == 0)
{

View File

@@ -54,3 +54,4 @@ void main()
effect eStatue = EffectPetrify();
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eStatue, OBJECT_SELF);
}

View File

@@ -11,17 +11,10 @@
//:: Created On: September 2003
//:://////////////////////////////////////////////
#include "x2_inc_ws_smith"
//#include "x2_inc_cutscene"
//void StartCutscene(object oPC);
//void StartCutscene2(object oPC);
void CopyUpgradeItem(object oItem, object oPC);
void MakeNewWeapon(object oPC);
//int nCutsceneNumber = 114;
void main()
{
object oPC = GetPCSpeaker();
@@ -33,273 +26,8 @@ void main()
{
return;
}
//ActionPauseConversation();
//if(GetTag(GetArea(oPC)) != "HellbreathTavern")
{
//Only show the cutscene once
//if (GetLocalInt(oPC, "X2_Chapter2SmithCut") == 1)
//{
// MakeNewWeapon(oPC);
// return;
// }
// SetLocalInt(oPC, "X2_Chapter2SmithCut", 1);
// DelayCommand(0.7, StartCutscene(oPC));
//}
//else
//{
//Only show the cutscene once
//if (GetLocalInt(oPC, "X2_Chapter3SmithCut") == 1)
//{
//MakeNewWeapon(oPC);
//return;
//}
// SetLocalInt(oPC, "X2_Chapter3SmithCut", 1);
// DelayCommand(0.7, StartCutscene2(oPC));
//}
//Set Cutscene 114 as active for all future calls to Cut_ commands
//CutSetActiveCutscene(nCutsceneNumber, CUT_DELAY_TYPE_CONSTANT);
//AssignCommand(oPC, ClearAllActions());
//Fade PCs to black
//BlackScreen(oPC);
//CutSetActiveCutsceneForObject(oPC, nCutsceneNumber, TRUE);
//CutDisableAbort(nCutsceneNumber);
//CutSetCutsceneMode(0.5, oPC, TRUE, TRUE, TRUE, TRUE); // pc invis - keep and freeze associates
//CutFadeFromBlack(1.5, oPC, FADE_SPEED_MEDIUM, FALSE);
//}
//void StartCutscene(object oPC)
//{
//Change the Music
//object oArea = GetArea(oPC);
//MusicBattlePlay(oArea);
// Cutscene actors and objects.
// Invisible creature "takes" item
//location lLoc = GetLocation(GetNearestObjectByTag("SpawnHere"));
//object oHelper = CreateObject(OBJECT_TYPE_CREATURE, "x2_helper", lLoc);
//object oSmith = OBJECT_SELF;
//object oForge = GetNearestObjectByTag("x2_magic_forge");
//CutSetActiveCutsceneForObject(oHelper, nCutsceneNumber);
//CutSetActiveCutsceneForObject(oSmith, nCutsceneNumber);
//CutSetActiveCutsceneForObject(oForge, nCutsceneNumber);
//CutApplyEffectToObject2(0.0, DURATION_TYPE_PERMANENT, EffectCutsceneGhost(), oHelper);
//Locations
//location lForge = GetLocation(GetNearestObjectByTag("X2_Forge"));
//Camera waypoints
//object oCamera1 = GetNearestObjectByTag("wp_cut114_camera1");
//location lCamera1 = GetLocation(GetNearestObjectByTag("wp_cut114_camera1"));
//location lCamera2 = GetLocation(GetNearestObjectByTag("wp_cut114_camera2"));
//Make a copy of the PC to move about
//location lStart = GetLocation(oPC);
//object oCopy = CutCreatePCCopy(oPC, lStart, "Cut114PCCopy");
//ChangeToStandardFaction(oCopy, STANDARD_FACTION_COMMONER);
//CutSetActiveCutsceneForObject(oCopy, nCutsceneNumber);
// Camera movements (includes moving the PC as the camera.
//////////////////////////////////////////////////////////
//CutJumpToObject(0.0, oPC, oCamera1, TRUE);
//CutSetCamera(0.0, oPC, CAMERA_MODE_TOP_DOWN, 90.0, 10.0, 75.0,
// CAMERA_TRANSITION_TYPE_SNAP);
//CutSetCamera(0.2, oPC, CAMERA_MODE_TOP_DOWN, 150.0, 13.0, 50.0,
//CAMERA_TRANSITION_TYPE_SLOW);
//CutActionMoveToLocation(0.2, oPC, lCamera2, FALSE, FALSE);
// Smith chants
//CutPlayAnimation(0.7, oSmith, ANIMATION_LOOPING_MEDITATE, 6.0, FALSE);
// CutPlaySound(0.5, oSmith, "al_mg_chntmagic1", FALSE);
// * Make creature invisible and non bumpable
// effect eInvis = EffectVisualEffect(VFX_DUR_CUTSCENE_INVISIBILITY);
// ApplyEffectToObject(DURATION_TYPE_PERMANENT, eInvis, oHelper);
//effect eNoBump = EffectCutsceneGhost();
//CutApplyEffectToObject2(0.0, DURATION_TYPE_PERMANENT, eNoBump, oHelper);
//object oItem = GetRightHandWeapon(oCopy);
// object oItem2 = GetRightHandWeapon(oPC);
// SpeakString("OLD " + GetName(oItem));
// object oNewItem = CopyItem(oItem, oHelper, TRUE);
// SpeakString("NEW " + GetName(oNewItem));
//AssignCommand(oPC, ActionUnequipItem(oItem)); // * unequip the item
//object oNewItem = CopyItem(oItem, oCopy, TRUE);
// DelayCommand(2.0, AssignCommand(oCopy, ActionGiveItem(oNewItem, oHelper)));
//DestroyObject(oItem, 2.0);
//DestroyObject(oItem2, 2.0);
//CutClearAllActions(1.5, oHelper, FALSE);
//CutActionEquipItem(2.4, oHelper, oNewItem, INVENTORY_SLOT_RIGHTHAND);
// Runs up to forge
//CutActionMoveToLocation(3.0, oHelper, lForge, FALSE, FALSE);
// * Need visual effects to hide the sword moving from you to the object
// Light up Forge
// effect eStrike = EffectVisualEffect(VFX_FNF_STRIKE_HOLY);
//effect eShake = EffectVisualEffect(VFX_FNF_SCREEN_BUMP);
//effect eLink = EffectLinkEffects(eStrike, eShake);
//CutApplyEffectToObject2(5.0, DURATION_TYPE_INSTANT, eLink, oForge);
//CutPlayAnimation(5.0, oForge, ANIMATION_PLACEABLE_ACTIVATE, 5.0, FALSE);
// effect eRing = EffectVisualEffect(VFX_DUR_ELEMENTAL_SHIELD);
// CutApplyEffectToObject(6.1, DURATION_TYPE_TEMPORARY, VFX_DUR_ELEMENTAL_SHIELD, oHelper, 0.7);
// Item is enhanced
//DelayCommand(6.0, wsEnhanceItem(oHelper, oPC));
//Copy the upgraded item into the PCs inventory
//DelayCommand(7.0, CopyUpgradeItem(oNewItem, oPC));
//CutPlayAnimation(6.0, oHelper, ANIMATION_FIREFORGET_VICTORY2, 3.0, FALSE);
// Weapon Given back to player
//CutClearAllActions(7.5, oHelper, FALSE, FALSE);
//CutActionMoveToObject(8.0, oHelper, oCopy, TRUE);
// DelayCommand(9.7, AssignCommand(oHelper, ActionGiveItem(oNewItem, oCopy)));
//DelayCommand(11.0, AssignCommand(oCopy, ActionEquipItem(oNewItem, INVENTORY_SLOT_RIGHTHAND)));
//CutPlayAnimation(12.0, oCopy, ANIMATION_FIREFORGET_VICTORY2, 2.0);
// End Cutscene
//CutFadeOutAndIn(13.0, oPC);
//Clean up actors...
//CutDestroyObject(14.3, oHelper);
//CutDisableCutscene(nCutsceneNumber, 14.5, 14.5, RESTORE_TYPE_COPY);
// * Resume conversation - Cleanup
//DelayCommand(15.5, ActionResumeConversation());
}
//void StartCutscene2(object oPC)
{
//Change the Music
//object oArea = GetArea(oPC);
//MusicBattlePlay(oArea);
// Cutscene actors and objects.
// Invisible creature "takes" item
//location lLoc = GetLocation(GetNearestObjectByTag("SpawnHere"));
//object oHelper = CreateObject(OBJECT_TYPE_CREATURE, "x2_helper", lLoc);
//object oSmith = OBJECT_SELF;
//object oForge = GetNearestObjectByTag("x2_magic_forge");
//CutSetActiveCutsceneForObject(oHelper, nCutsceneNumber);
//CutSetActiveCutsceneForObject(oSmith, nCutsceneNumber);
//CutSetActiveCutsceneForObject(oForge, nCutsceneNumber);
//Locations
// location lForge = GetLocation(GetNearestObjectByTag("X2_Forge"));
//Camera waypoints
//object oCamera1 = GetNearestObjectByTag("wp_cut114_camera1");
// location lCamera1 = GetLocation(GetNearestObjectByTag("wp_cut114_camera1"));
//location lCamera2 = GetLocation(GetNearestObjectByTag("wp_cut114_camera2"));
//Make a copy of the PC to move about
//location lStart = GetLocation(GetNearestObjectByTag("hx_smith_wp", oPC));
//object oCopy = CutCreatePCCopy(oPC, lStart, "Cut114PCCopy");
//ChangeToStandardFaction(oCopy, STANDARD_FACTION_COMMONER);
//CutSetActiveCutsceneForObject(oCopy, nCutsceneNumber);
// Camera movements (includes moving the PC as the camera.
//////////////////////////////////////////////////////////
//CutJumpToObject(0.0, oPC, oCamera1, TRUE);
//CutSetCamera(0.0, oPC, CAMERA_MODE_TOP_DOWN, 40.0, 6.0, 80.0,
// CAMERA_TRANSITION_TYPE_SNAP);
// CutSetCamera(0.2, oPC, CAMERA_MODE_TOP_DOWN, 40.0, 3.0, 80.0,
// CAMERA_TRANSITION_TYPE_VERY_SLOW);
// Smith chants
//CutPlayAnimation(0.7, oSmith, ANIMATION_LOOPING_MEDITATE, 6.0, FALSE);
//CutPlaySound(0.5, oSmith, "al_mg_chntmagic1", FALSE);
// * Make creature invisible and non bumpable
// effect eInvis = EffectVisualEffect(VFX_DUR_CUTSCENE_INVISIBILITY);
// ApplyEffectToObject(DURATION_TYPE_PERMANENT, eInvis, oHelper);
//effect eNoBump = EffectCutsceneGhost();
// CutApplyEffectToObject2(0.0, DURATION_TYPE_PERMANENT, eNoBump, oHelper);
// object oItem = GetRightHandWeapon(oCopy);
// object oItem2 = GetRightHandWeapon(oPC);
// SpeakString("OLD " + GetName(oItem));
// object oNewItem = CopyItem(oItem, oHelper, TRUE);
// SpeakString("NEW " + GetName(oNewItem));
//AssignCommand(oPC, ActionUnequipItem(oItem)); // * unequip the item
//object oNewItem = CopyItem(oItem, oCopy, TRUE);
//
//DelayCommand(2.0, AssignCommand(oCopy, ActionGiveItem(oNewItem, oHelper)));
// DestroyObject(oItem, 2.0);
// DestroyObject(oItem2, 2.0);
//CutClearAllActions(1.5, oHelper, FALSE);
//CutActionEquipItem(2.4, oHelper, oNewItem, INVENTORY_SLOT_RIGHTHAND);
// Runs up to forge
//CutActionMoveToLocation(3.0, oHelper, lForge, FALSE, FALSE);
// * Need visual effects to hide the sword moving from you to the object
// Light up Forge
//effect eStrike = EffectVisualEffect(VFX_FNF_STRIKE_HOLY);
//effect eShake = EffectVisualEffect(VFX_FNF_SCREEN_BUMP);
//effect eLink = EffectLinkEffects(eStrike, eShake);
//CutApplyEffectToObject2(5.0, DURATION_TYPE_INSTANT, eLink, oForge);
//CutPlayAnimation(5.0, oForge, ANIMATION_PLACEABLE_ACTIVATE, 5.0, FALSE);
//effect eRing = EffectVisualEffect(VFX_DUR_ELEMENTAL_SHIELD);
//CutApplyEffectToObject(6.1, DURATION_TYPE_TEMPORARY, VFX_DUR_ELEMENTAL_SHIELD, oHelper, 0.7);
// Item is enhanced
// DelayCommand(6.0, wsEnhanceItem(oHelper, oPC));
//Copy the upgraded item into the PCs inventory
//DelayCommand(7.0, CopyUpgradeItem(oNewItem, oPC));
//CutPlayAnimation(6.0, oHelper, ANIMATION_FIREFORGET_VICTORY2, 3.0, FALSE);
// Weapon Given back to player
//CutClearAllActions(7.5, oHelper, FALSE, FALSE);
//CutActionMoveToObject(8.0, oHelper, oCopy, TRUE);
//DelayCommand(9.7, AssignCommand(oHelper, ActionGiveItem(oNewItem, oCopy)));
//DelayCommand(11.0, AssignCommand(oCopy, ActionEquipItem(oNewItem, INVENTORY_SLOT_RIGHTHAND)));
// CutPlayAnimation(12.0, oCopy, ANIMATION_FIREFORGET_VICTORY2, 2.0);
// End Cutscene
//CutFadeOutAndIn(13.0, oPC);
//Clean up actors...
//CutDestroyObject(14.3, oHelper);
//CutDisableCutscene(nCutsceneNumber, 14.5, 14.5, RESTORE_TYPE_COPY);
// * Resume conversation - Cleanup
// DelayCommand(15.5, ActionResumeConversation());
//}
void CopyUpgradeItem(object oItem, object oPC)
{
object oNewItem = CopyItem(oItem, oPC, TRUE);