NWNDS/nwnds_module/hench_i0_equip.nss
Jaysyn904 de24f81734 Added NWN Dark Sun module contents
Added NWN Dark Sun module contents.
2021-07-12 21:24:46 -04:00

771 lines
22 KiB
Plaintext

/*
Henchman Inventory And Battle AI
This file contains routines used in equipping monsters
and henchman. Contains modified EquipAppropriateWeapons
function (renamed HenchEquipAppropriateWeapons)
*/
#include "hench_i0_generic"
// void main() { }
// internal weapon state settings
const int HENCH_AI_WEAPON_INIT = 0x00000001;
const int HENCH_AI_HAS_MELEE = 0x00000002;
const int HENCH_AI_HAS_MELEE_WEAPON = 0x00000004;
const int HENCH_AI_HAS_RANGED_WEAPON = 0x00000008;
const string HENCH_AI_WEAPON = "HENCH_AI_WEAPON";
const string HENCH_AI_STORED_MELEE = "HenchStoredMeleeWeapon";
const string HENCH_AI_STORED_RANGED = "HenchStoredRangedWeapon";
const string HENCH_AI_STORED_SHIELD = "StoredShield";
const string HENCH_AI_SHIELD_STATUS = "HaveShieldStatus";
const string HENCH_AI_STORED_OFF_HAND = "StoredOffHand";
const string HENCH_AI_OFF_HAND_STATUS = "HaveOffhandStatus";
const string HENCH_AI_SWITCHED_TO_MELEE = "SwitchedToMelee";
const string HENCH_AI_COMBAT_EQUIP = "HenchCombatEquip";
// void main() { }
// cache weapon state information on self
void SetWeaponState(int nCondition, int bValid = TRUE);
// un-equip weapons from left and right hands
void UnequipWeapons();
// equip best shield available
void EquipShield(int bIndicateStatus);
// get cached weapon state information
int GetInitWeaponStatus();
// equip best melee weapon(s) against target AND check for shield
int EquipMeleeWeapons(object oTarget, int bIndicateStatus, int iCallNumber);
// equip best ranged weapon(s) against target
int EquipRangedWeapon(object oTarget, int bIndicateStatus, int iCallNumber);
// equip best weapons based on target and threshold distance
int HenchEquipAppropriateWeapons(object oTarget, float fThresholdDistance, int bIndicateSwitch, int bPolymorphed);
// continue weapon equipping that is in progress
void ActionContinueEquip(object oTarget, int bIndicateStatus, int iCallNumber);
// wrapper to equip default weapons
void HenchEquipDefaultWeapons(object oCreature = OBJECT_SELF, int bShowStatus = FALSE);
// clear all cached weapon state information
void ClearWeaponStates(object oCreature = OBJECT_SELF);
// equip weapons on self
void ActionChangeEquippedWeapons();
// equip weapons on target creature
void ChangeEquippedWeapons(object oCreature);
void SetWeaponState(int nCondition, int bValid = TRUE)
{
int nStatus = GetLocalInt(OBJECT_SELF, HENCH_AI_WEAPON);
if (bValid)
{
nStatus = nStatus | nCondition;
SetLocalInt(OBJECT_SELF, HENCH_AI_WEAPON, nStatus);
}
else
{
nStatus = nStatus & ~nCondition;
SetLocalInt(OBJECT_SELF, HENCH_AI_WEAPON, nStatus);
}
}
void UnequipWeapons()
{
object oRight = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND);
if (GetIsObjectValid(oRight))
{
ActionUnequipItem(oRight);
}
object oLeft = GetItemInSlot(INVENTORY_SLOT_LEFTHAND);
if (GetIsObjectValid(oLeft))
{
ActionUnequipItem(oLeft);
}
}
void EquipShield(int bIndicateStatus)
{
int iHaveShieldStatus = GetLocalInt(OBJECT_SELF, HENCH_AI_SHIELD_STATUS);
object oShield = GetItemInSlot(INVENTORY_SLOT_LEFTHAND);
if (iHaveShieldStatus == 2)
{
// not really a shield, get rid of
if (GetIsObjectValid(oShield))
{
ActionUnequipItem(oShield);
}
return;
}
if (iHaveShieldStatus == 1)
{
object oStoredShield = GetLocalObject(OBJECT_SELF, HENCH_AI_STORED_SHIELD);
if (oStoredShield == oShield)
{
return;
}
if (GetIsObjectValid(oStoredShield) && (GetItemPossessor(oStoredShield) == OBJECT_SELF))
{
ActionEquipItem(oStoredShield, INVENTORY_SLOT_LEFTHAND);
return;
}
}
int nMaxValue = 0;
int iCreatureSize = GetCreatureSize(OBJECT_SELF);
object oRealMaster = GetRealMaster();
int bNoPCMaster = !(GetIsObjectValid(oRealMaster) && GetIsPC(oRealMaster));
if (GetIsObjectValid(oShield))
{
int nSize = 1; // note start at one less so easy compare with creature size
switch (GetBaseItemType(oShield))
{
case BASE_ITEM_TOWERSHIELD:
nSize ++;
case BASE_ITEM_LARGESHIELD:
nSize ++;
case BASE_ITEM_SMALLSHIELD:
if (bNoPCMaster || GetIdentified(oShield))
{
int bPlotFlag = GetPlotFlag(oShield);
if (bPlotFlag)
{
SetPlotFlag(oShield, FALSE);
}
nMaxValue = GetGoldPieceValue(oShield);
if (bPlotFlag)
{
SetPlotFlag(oShield, TRUE);
}
}
else
{
nMaxValue = 2;
}
break;
/* case BASE_ITEM_TORCH:
nMaxValue = 1;
break; */
default:
// not a shield - remove
ActionUnequipItem(oShield);
oShield = OBJECT_INVALID;
break;
}
}
int iNewShield = FALSE;
int tempValue;
int bHasShieldProf = GetHasFeat(FEAT_SHIELD_PROFICIENCY);
int bCantUseShield = FALSE;
// int bCanUseTorch = (iCreatureSize > 1) && GetCreatureUseItems(OBJECT_SELF);
object oItem = GetFirstItemInInventory();
while (GetIsObjectValid(oItem))
{
// skip past any items in a container
if (GetHasInventory(oItem))
{
object oContainer = oItem;
object oSubItem = GetFirstItemInInventory(oContainer);
oItem = GetNextItemInInventory();
while (GetIsObjectValid(oSubItem))
{
oItem = GetNextItemInInventory();
oSubItem = GetNextItemInInventory(oContainer);
}
continue;
}
int nSize = 1; // note start at one less so easy compare with creature size
switch (GetBaseItemType(oItem))
{
case BASE_ITEM_TOWERSHIELD:
nSize ++;
case BASE_ITEM_LARGESHIELD:
nSize ++;
case BASE_ITEM_SMALLSHIELD:
if (bHasShieldProf && (iCreatureSize >= nSize))
{
if (bNoPCMaster || GetIdentified(oItem))
{
int bPlotFlag = GetPlotFlag(oItem);
if (bPlotFlag)
{
SetPlotFlag(oItem, FALSE);
}
tempValue = GetGoldPieceValue(oItem);
if (bPlotFlag)
{
SetPlotFlag(oItem, TRUE);
}
}
else
{
tempValue = 2;
}
if (tempValue > nMaxValue)
{
nMaxValue = tempValue;
oShield = oItem;
iNewShield = TRUE;
}
}
else
{
bCantUseShield = TRUE;
}
break;
/* case BASE_ITEM_TORCH:
if (bCanUseTorch && (1 > nMaxValue))
{
nMaxValue = 1;
oShield = oItem;
iNewShield = TRUE;
} */
break;
default:
break;
}
oItem = GetNextItemInInventory();
}
if (nMaxValue > 0)
{
SetLocalInt(OBJECT_SELF, HENCH_AI_SHIELD_STATUS, 1);
SetLocalObject(OBJECT_SELF, HENCH_AI_STORED_SHIELD, oShield);
}
else
{
SetLocalInt(OBJECT_SELF, HENCH_AI_SHIELD_STATUS, 2);
DeleteLocalObject(OBJECT_SELF, HENCH_AI_STORED_SHIELD);
}
if (iNewShield)
{
ActionEquipItem(oShield, INVENTORY_SLOT_LEFTHAND);
}
else if (bCantUseShield && bIndicateStatus)
{
SpeakString(sHenchCantUseShield);
}
}
int GetMeleeWeaponSize(object oItem)
{
if (GetWeaponRanged(oItem))
{
return 0;
}
int nBase = GetBaseItemType(oItem);
if (nBase == BASE_ITEM_TORCH)
{
return 0;
}
string sWeaponSizeStr = "HENCH_AI_WEAPON_SIZE_" + IntToString(nBase);
object oModule = GetModule();
int iWeaponSize = GetLocalInt(oModule, sWeaponSizeStr);
if (iWeaponSize == 0)
{
iWeaponSize = StringToInt(Get2DAString("baseitems", "WeaponSize", nBase));
if (iWeaponSize == 0)
{
iWeaponSize = -1;
}
SetLocalInt(oModule, sWeaponSizeStr, iWeaponSize);
}
if (iWeaponSize > 0)
{
return iWeaponSize;
}
return 0;
}
int GetInitWeaponStatus()
{
int nResult = GetLocalInt(OBJECT_SELF, HENCH_AI_WEAPON);
if (nResult)
{
return nResult;
}
int bHasMeleeWeapon;
int bHasMeleeAttack;
int bHasRangedWeapon;
object oItem = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND);
if (GetIsObjectValid(oItem))
{
if (GetWeaponRanged(oItem))
{
bHasRangedWeapon = TRUE;
}
else if (GetMeleeWeaponSize(oItem) > 0)
{
bHasMeleeWeapon = TRUE;
}
}
oItem = GetFirstItemInInventory();
while (GetIsObjectValid(oItem) && !(bHasMeleeWeapon && bHasRangedWeapon))
{
// skip past any items in a container
if (GetHasInventory(oItem))
{
object oContainer = oItem;
object oSubItem = GetFirstItemInInventory(oContainer);
oItem = GetNextItemInInventory();
while (GetIsObjectValid(oSubItem))
{
oItem = GetNextItemInInventory();
oSubItem = GetNextItemInInventory(oContainer);
}
continue;
}
if (!bHasRangedWeapon && GetWeaponRanged(oItem))
{
bHasRangedWeapon = TRUE;
}
else if (!bHasMeleeWeapon && GetMeleeWeaponSize(oItem) > 0)
{
bHasMeleeWeapon = TRUE;
}
oItem = GetNextItemInInventory();
}
if (!bHasMeleeWeapon)
{
if (GetHasFeat(FEAT_IMPROVED_UNARMED_STRIKE))
{
bHasMeleeAttack = TRUE;
}
else if (GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_CWEAPON_R)))
{
bHasMeleeAttack = TRUE;
}
else if (GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_CWEAPON_B)))
{
bHasMeleeAttack = TRUE;
}
else if (GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_CWEAPON_L)))
{
bHasMeleeAttack = TRUE;
}
}
else
{
bHasMeleeAttack = TRUE;
}
if (bHasMeleeAttack)
{
nResult = HENCH_AI_WEAPON_INIT | HENCH_AI_HAS_MELEE;
}
else
{
nResult = HENCH_AI_WEAPON_INIT;
}
if (bHasMeleeWeapon)
{
nResult = nResult | HENCH_AI_HAS_MELEE_WEAPON;
}
if (bHasRangedWeapon)
{
nResult = nResult | HENCH_AI_HAS_RANGED_WEAPON;
}
SetLocalInt(OBJECT_SELF, HENCH_AI_WEAPON, nResult);
return nResult;
}
int EquipMeleeWeapons(object oTarget, int bIndicateStatus, int iCallNumber)
{
// float time = IntToFloat(GetTimeSecond()) + IntToFloat(GetTimeMillisecond()) /1000.0;
// Jug_Debug(GetName(OBJECT_SELF) + " equip melee vs. " + GetName(oTarget) + " call number " + IntToString(iCallNumber) + " time " + FloatToString(time));
int nWeaponStatus = GetInitWeaponStatus();
if (iCallNumber == 1)
{
if (!(nWeaponStatus & HENCH_AI_HAS_MELEE_WEAPON))
{
if ((nWeaponStatus & HENCH_AI_HAS_MELEE))
{
// no weapons, has creature attacks, make sure ranged weapons removed
UnequipWeapons();
}
return TRUE;
}
ActionEquipMostDamagingMelee(oTarget);
return FALSE;
}
object oRight = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND);
int iCreatureSize = GetCreatureSize(OBJECT_SELF);
int iRWeaponSize = GetMeleeWeaponSize(oRight);
if (iRWeaponSize > iCreatureSize)
{
// two handed weapon - done
return TRUE;
}
// for dual weapon selection, must wait until right weapon is equipped
if (!GetIsObjectValid(oRight))
{
if (nWeaponStatus & HENCH_AI_HAS_MELEE)
{
return TRUE;
}
else
{
SetWeaponState(HENCH_AI_HAS_MELEE_WEAPON, FALSE);
return TRUE;
}
}
int dualWieldState = GetLocalInt(OBJECT_SELF, sHenchDualWieldState);
if (dualWieldState == 0)
{
dualWieldState = GetHasFeat(FEAT_TWO_WEAPON_FIGHTING) ? 1 : 2;
}
if (dualWieldState == 2)
{
EquipShield(bIndicateStatus);
return TRUE;
}
// already have something in left
object oOrigLeft = GetItemInSlot(INVENTORY_SLOT_LEFTHAND);
int iHaveOffHandStatus = GetLocalInt(OBJECT_SELF, HENCH_AI_OFF_HAND_STATUS);
if (iHaveOffHandStatus == 2)
{
EquipShield(bIndicateStatus);
return TRUE;
}
if (iHaveOffHandStatus == 1)
{
object oStoredOffHand = GetLocalObject(OBJECT_SELF, HENCH_AI_STORED_OFF_HAND);
if (oStoredOffHand == oOrigLeft)
{
return TRUE;
}
if (GetIsObjectValid(oStoredOffHand) && (GetItemPossessor(oStoredOffHand) == OBJECT_SELF) &&
oRight != oStoredOffHand)
{
ActionEquipItem(oStoredOffHand, INVENTORY_SLOT_LEFTHAND);
return TRUE;
}
}
object oLeft = OBJECT_INVALID;
int nLeftPrevEquip = GetIsObjectValid(oOrigLeft);
int nMaxValue = 0;
int iMaxWeaponSize = iCreatureSize;
if (GetLocalInt(OBJECT_SELF, sHenchLightOffHand) && (iRWeaponSize >= iCreatureSize))
{
iMaxWeaponSize--;
}
int iCurWeaponSize;
if (nLeftPrevEquip)
{
iCurWeaponSize = GetMeleeWeaponSize(oOrigLeft);
if (iCurWeaponSize != 0 && iCurWeaponSize <= iMaxWeaponSize)
{
if (GetIdentified(oOrigLeft))
{
int bPlotFlag = GetPlotFlag(oOrigLeft);
if (bPlotFlag)
{
SetPlotFlag(oOrigLeft, FALSE);
}
nMaxValue = GetGoldPieceValue(oOrigLeft);
if (bPlotFlag)
{
SetPlotFlag(oOrigLeft, TRUE);
}
}
else
{
nMaxValue = 1;
}
oLeft = oOrigLeft;
}
}
// Then look for more than 1 single handed melee weapon
int iNewOffHand = FALSE;
object oItem = GetFirstItemInInventory();
while (GetIsObjectValid(oItem))
{
// skip past any items in a container
if (GetHasInventory(oItem))
{
object oContainer = oItem;
object oSubItem = GetFirstItemInInventory(oContainer);
oItem = GetNextItemInInventory();
while (GetIsObjectValid(oSubItem))
{
oItem = GetNextItemInInventory();
oSubItem = GetNextItemInInventory(oContainer);
}
continue;
}
int nItemType = GetBaseItemType(oItem);
if (nItemType != BASE_ITEM_LIGHTFLAIL && nItemType != BASE_ITEM_MORNINGSTAR &&
nItemType != BASE_ITEM_WHIP)
{
iCurWeaponSize = GetMeleeWeaponSize(oItem);
if (iCurWeaponSize != 0 && iCurWeaponSize <= iMaxWeaponSize)
{
int tempValue;
if (GetIdentified(oItem))
{
int bPlotFlag = GetPlotFlag(oItem);
if (bPlotFlag)
{
SetPlotFlag(oItem, FALSE);
}
tempValue = GetGoldPieceValue(oItem);
if (bPlotFlag)
{
SetPlotFlag(oItem, TRUE);
}
}
else
{
tempValue = 1;
}
if (tempValue > nMaxValue)
{
nMaxValue = tempValue;
oLeft = oItem;
iNewOffHand = TRUE;
}
}
}
oItem = GetNextItemInInventory();
}
if (nMaxValue > 0)
{
SetLocalInt(OBJECT_SELF, HENCH_AI_OFF_HAND_STATUS, 1);
SetLocalObject(OBJECT_SELF, HENCH_AI_STORED_OFF_HAND, oLeft);
}
else
{
SetLocalInt(OBJECT_SELF, HENCH_AI_OFF_HAND_STATUS, 2);
DeleteLocalObject(OBJECT_SELF, HENCH_AI_STORED_OFF_HAND);
}
if (iNewOffHand)
{
ActionEquipItem(oLeft, INVENTORY_SLOT_LEFTHAND);
}
else if (GetIsObjectValid(oOrigLeft) && oOrigLeft == oLeft)
{
// nothing to do
}
else
{
EquipShield(bIndicateStatus);
}
return TRUE;
}
int EquipRangedWeapon(object oTarget, int bIndicateStatus, int iCallNumber)
{
// float time = IntToFloat(GetTimeSecond()) + IntToFloat(GetTimeMillisecond()) /1000.0;
//Jug_Debug(GetName(OBJECT_SELF) + " equip ranged vs. " + GetName(oTarget) + " call number " + IntToString(iCallNumber) + " time " + FloatToString(time));
if (iCallNumber == 1)
{
ActionEquipMostDamagingRanged(oTarget);
return FALSE;
}
object oRight = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND);
if (!GetWeaponRanged(oRight))
{
if (iCallNumber == 2)
{
ActionWait(0.3);
return FALSE;
}
// object oRealMaster = GetRealMaster();
// if (GetIsObjectValid(oRealMaster) && GetIsPC(oRealMaster))
// {
// SpeakString(sHenchCantUseRanged);
// }
// Jug_Debug(GetName(OBJECT_SELF) + " turning off ranged weapons");
SetWeaponState(HENCH_AI_HAS_RANGED_WEAPON, FALSE);
SetLocalInt(OBJECT_SELF, "UseRangedWeapons", FALSE);
return EquipMeleeWeapons(oTarget, bIndicateStatus, 1);
}
switch (GetBaseItemType(oRight))
{
case BASE_ITEM_DART:
case BASE_ITEM_SHURIKEN:
case BASE_ITEM_SLING:
case BASE_ITEM_THROWINGAXE:
EquipShield(bIndicateStatus);
break;
}
return TRUE;
}
//::///////////////////////////////////////////////
//:: Equip Appropriate Weapons
//:: Copyright (c) 2001 Bioware Corp.
//:://////////////////////////////////////////////
/*
Makes the user get his best weapons. If the
user is a Henchmen then he checks the player
preference.
*/
//:://////////////////////////////////////////////
//:: Created By: Preston Watamaniuk
//:: Created On: April 2, 2002
//:://////////////////////////////////////////////
int HenchEquipAppropriateWeapons(object oTarget, float fThresholdDistance, int bIndicateSwitch, int bPolymorphed)
{
// Jug_Debug(GetName(OBJECT_SELF) + " trying out equip");
if (bPolymorphed)
{
return TRUE;
}
int nWeaponStatus = GetInitWeaponStatus();
int bUseRanged;
if (nWeaponStatus & HENCH_AI_HAS_RANGED_WEAPON)
{
// has ranged weapons
object oRealMaster = GetRealMaster();
if(GetIsObjectValid(oRealMaster) && GetIsPC(oRealMaster) && !GetAssociateState(NW_ASC_USE_RANGED_WEAPON))
{
bUseRanged = FALSE;
}
else
{
if (nWeaponStatus & HENCH_AI_HAS_MELEE)
{
// if has melee weapons (includes creature weapons & monk class)
// if z distance is greater than two then assume cliff
// TODO add size of creatures for distance?
bUseRanged = (GetDistanceToObject(oTarget) > fThresholdDistance) ||
(fabs(GetPosition(OBJECT_SELF).z - GetPosition(oTarget).z) > 2.0);
}
else
{
bUseRanged = TRUE;
}
}
}
else
{
bUseRanged = FALSE;
}
if (bIndicateSwitch && GetAssociateState(NW_ASC_USE_RANGED_WEAPON))
{
if (!(nWeaponStatus & HENCH_AI_HAS_RANGED_WEAPON))
{
DeleteLocalInt(OBJECT_SELF, HENCH_AI_SWITCHED_TO_MELEE);
}
else
{
int bSwitchedToMelee = GetLocalInt(OBJECT_SELF, HENCH_AI_SWITCHED_TO_MELEE);
if (bUseRanged && bSwitchedToMelee)
{
SpeakString(sHenchSwitchToMissle);
DeleteLocalInt(OBJECT_SELF, HENCH_AI_SWITCHED_TO_MELEE);
}
else if (!bUseRanged && !bSwitchedToMelee)
{
SpeakString(sHenchSwitchToRanged);
SetLocalInt(OBJECT_SELF, HENCH_AI_SWITCHED_TO_MELEE, TRUE);
}
}
}
SetLocalInt(OBJECT_SELF, "UseRangedWeapons", bUseRanged);
if (bUseRanged)
{
object oRight = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND);
if (!GetWeaponRanged(oRight))
{
object oLeft = GetItemInSlot(INVENTORY_SLOT_LEFTHAND);
if (GetIsObjectValid(oLeft))
{
// get rid of weapon or shield
// shield or weapon in left hand prevents switch to ranged
ActionUnequipItem(oLeft);
ActionWait(0.3);
}
}
return EquipRangedWeapon(oTarget, bIndicateSwitch, 1);
}
else
{
return EquipMeleeWeapons(oTarget, bIndicateSwitch, 1);
}
}
void ActionContinueEquip(object oTarget, int bIndicateStatus, int iCallNumber)
{
if (GetLocalInt(OBJECT_SELF, "UseRangedWeapons"))
{
if (!EquipRangedWeapon(oTarget, bIndicateStatus, iCallNumber))
{
ActionDoCommand(ActionContinueEquip(oTarget, bIndicateStatus, iCallNumber + 1));
}
}
else
{
if (!EquipMeleeWeapons(oTarget, bIndicateStatus, iCallNumber))
{
ActionDoCommand(ActionContinueEquip(oTarget, bIndicateStatus, iCallNumber + 1));
}
}
}
const string sHenchShowWeaponStatus = "HenchShowWeaponStatus";
void HenchEquipDefaultWeapons(object oCreature = OBJECT_SELF, int bShowStatus = FALSE)
{
if (bShowStatus)
{
SetLocalInt(oCreature, sHenchShowWeaponStatus, TRUE);
}
ExecuteScript("hench_o0_equip", oCreature);
}
void ClearWeaponStates(object oCreature = OBJECT_SELF)
{
DeleteLocalInt(oCreature, HENCH_AI_SWITCHED_TO_MELEE);
DeleteLocalInt(oCreature, HENCH_AI_SHIELD_STATUS);
DeleteLocalObject(oCreature, HENCH_AI_STORED_SHIELD);
DeleteLocalInt(oCreature, HENCH_AI_OFF_HAND_STATUS);
DeleteLocalObject(oCreature, HENCH_AI_STORED_OFF_HAND);
DeleteLocalInt(oCreature, HENCH_AI_WEAPON);
}