/* 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); }