Alangara_PRC8/_module/nss/x3_pl_tool01.nss
Jaysyn904 86feb9ca6f Initial commit
Initial commit.
2024-06-05 21:21:06 -04:00

190 lines
6.3 KiB
Plaintext

/***
Balkoth's Minion Control (BMC)
- Allows some semblance of control over henchmen, summons,
animal companions, and familiars
- Should be attached to an instant feat like the player tools or an
item's activate item (long range)
- If targeted on a hostile creature, commands all minions to attack that creature
- If targeted on a location, commands all minions to move to that location
- If targeted on a friendly/neutral creature, commands all minions to move to
that creature's location. However, setting the "attackNeutral" variable to 1
will cause minions to attack neutral creatures if neutral creatures are targeted
- In order to avoid things like telling the minion to move to a location and then
having it decide to attack something instead, the AI needs to be temporarily disabled.
You will need to put the following lines of code at the very beginning of main for
seven creature events:
// Don't do anything if we have have been recently commanded
if (GetLocalInt(OBJECT_SELF, "commandstatus"))
{
return;
}
It literally just tells the AI to not run if we've been issued an order recently.
Which brings us to a second global variable below: fDisableAITimer It is set to
12.0 seconds by default (the length of time to which the AI will not try to do anything
outside of follow your commands) but can be changed as desired.
The seven creature events are listed below (along with the corresponding default
script for summons/familiars/animal companions -- NPC companions will usually
have different scripts)
OnHeartbeat (nw_c2_ac1)
OnPerceive (nw_c2_ac2)
OnCombatRoundEnd (nw_c2_ac3)
OnDialogue (nw_c2_ac4)
OnAttacked (nw_c2_ac5)
OnDamaged (nw_c2_ac6)
OnSpellCastAt (nw_c2_acb)
- Once you've copy/pasted those four lines to each of those events for any affected
creature, simply copy/paste this whole script into your desired player tool,
ensure the PC has access to that player tool somehow (player skin works),
and you're good to go
***/
#include "prc_inc_spells"
// Determines behavior when targeting a neutral creature. Setting this to non-zero will
// cause minions to attack the neutral creature while zero will cause minions to move to
// the neutral creature
int nAttackNeutral = 0;
// Determines how long the minion's AI will be disabled (meaning it worries only about
// moving to the proper location (and staying there) or attacking a particular target.
// Further commands issued during this state will simply renew the state, so it is
// typically only an issue if the minion kills its target as it won't seek out a new one
// unless commanded to do so or until its AI reactivates
float fDisableAITimer = 12.0;
void DoStuff(object oTarget, object oPC, location lTarget);
void MakeCommandable(int nCommand);
void TryToMove(location lTarget, int nCommand, float fTimer);
void main()
{
// This will need to be changed if an item is used instead of a player feat, hence
// why oPC is used rather than OBJECT_SELF throughout the code
object oPC = OBJECT_SELF, oTarget = PRCGetSpellTargetObject();
int nMinions;
// If we have a valid target, use that location, otherwise get the location
location lTarget = GetLocation(oTarget);
if (oTarget == OBJECT_INVALID)
{
lTarget = PRCGetSpellTargetLocation();
}
// Going to loop through members of the party
object oMinion = GetFirstFactionMember(oPC, FALSE);
while (GetIsObjectValid(oMinion))
{
// If the party member's master (or master's master for minion summons) is the PC, issue orders
if (GetMaster(oMinion) == oPC || GetMaster(GetMaster(oMinion)) == oPC)
{
nMinions = 1;
AssignCommand(oMinion, DoStuff(oTarget, oPC, lTarget));
}
oMinion = GetNextFactionMember(oPC, FALSE);
}
// Sends a message if activated without any minion to controll -- modify or remove
// message as desired
if (!nMinions)
{
FloatingTextStringOnCreature("This feat is used to issue orders to animal companions.", OBJECT_SELF, FALSE);
}
}
void DoStuff(object oTarget, object oPC, location lTarget)
{
int nCommand;
// Command stuff
nCommand = GetLocalInt(OBJECT_SELF, "commandvalue");
SetLocalInt(OBJECT_SELF, "commandstatus", 1);
if (nCommand == 99)
{
nCommand = 1;
}
else
{
(++nCommand)%100;
}
DelayCommand(fDisableAITimer, MakeCommandable(nCommand));
SetLocalInt(OBJECT_SELF, "commandvalue", nCommand);
ClearAllActions();
// Targeted on object
if (GetIsObjectValid(oTarget) && GetObjectType(oTarget) == OBJECT_TYPE_CREATURE)
{
// Attack enemies
if (GetIsEnemy(oTarget, oPC))
{
ActionAttack(oTarget);
}
// Attack or move to neutral depending on variable
else if (GetIsNeutral(oTarget, oPC))
{
if (nAttackNeutral)
{
ActionAttack(oTarget);
}
else
{
ActionMoveToObject(oTarget, TRUE);
}
}
// Move to friendly targets
else
{
ActionMoveToObject(oTarget, TRUE);
}
}
// Targeted on location
else
{
TryToMove(lTarget, nCommand, 0.0);
}
}
void MakeCommandable(int nCommand)
{
if (nCommand == GetLocalInt(OBJECT_SELF, "commandvalue"))
{
SetLocalInt(OBJECT_SELF, "commandstatus", 0);
}
}
void TryToMove(location lTarget, int nCommand, float fTimer)
{
// Has our command status changed? Have we timed out?
if (nCommand != GetLocalInt(OBJECT_SELF, "commandvalue") || fTimer >= fDisableAITimer)
{
return;
}
// Are we already there?
float fDist = GetDistanceBetweenLocations(lTarget, GetLocation(OBJECT_SELF));
if (fDist >= 0.0 && fDist < 1.0)
{
return;
}
// Only give the command if we're not moving
if (!GetActionMode(OBJECT_SELF, ACTION_MOVETOPOINT))
{
ActionMoveToLocation(lTarget, TRUE);
}
// Call again until we're there, commandvalue changes, or we time out
//SpeakString("nTimer is " + IntToString(nTimer));
DelayCommand(1.0, TryToMove(lTarget, nCommand, fTimer + 1.0));
}