BG3-Community-Library-Team / BG3-Community-Library

Baldur's Gate 3 Community Library exists to provide a common collection of Spells, Scripts, Items, Statuses, Passives, and other useful material that will assist modders in creating new content.
MIT License
90 stars 203 forks source link

[Feature] Updating the HasPactWeapon() function to recognise the new cl_pact_blade_### statuses #74

Closed Ragingg13 closed 11 months ago

Ragingg13 commented 11 months ago

Issue: the CL added 5 new status' for pact of the blade feature that replicate the pact_blade status functionality but for a different ability style. The statuses work great but are not recognised by the HasPactWeapon() function from game\scripts\thothzhelpers\commonconditionsdev.khn

Solution: Update the script to recognise the additional statuses added with the community library, in theory the code below achieves this but from my own testing it doesn't seem to make any difference. This would allow the non-charisma pact blade statuses from the CL to be recognised for features such as thirstingblade_blade that give pact weapons extra attack after level 5, it would also increase compatibility with any future mods that look for the HasPactWeapon() flag for applying invocations correctly.

local __util = require 'larian.util'

function HasPactWeapon(entity, mainHand)
    local entity = entity or context.Source
    local weaponEntity = GetActiveWeapon(entity, true)
    if weaponEntity.IsValid then
        result = Character(entity) & (HasStatus('PACT_BLADE', weaponEntity) or HasStatus('CL_PACT_BLADE_STR', weaponEntity) or HasStatus('CL_PACT_BLADE_DEX', weaponEntity) or HasStatus('CL_PACT_BLADE_INT', weaponEntity) or HasStatus('CL_PACT_BLADE_WIS', weaponEntity) or HasStatus('CL_PACT_BLADE_CON', weaponEntity))
        return ConditionResult(result.Result, {ConditionError("HasNotPactWeapon")}, {ConditionError("HasPactWeapon")})
    end
    return ConditionResult(false, {ConditionError("HasNotPactWeapon")}, {ConditionError("HasPactWeapon")})
end
Ragingg13 commented 11 months ago

On further investigation, it looks like Larian's version of the haspactweapon() function always loads second and clears changes to it I am not knowledgeable enough to know how to change that so the function changes load second. An alternative is to create a new function entirely and then replace all of the calls from thirstingblade_blade to call the new function although this is likely to break compatibility with other mods

So I guess unless someone knows how to get the function change to load after Larian's script this can be closed?

NellsRelo commented 11 months ago

Linked PR may have what's needed - I don't have the ability to test it at this time, but essentially I've set up two functions, one helper function, CL_HasStatusFromArray:

function CL_HasStatusFromArray(arr, entity)
  for arrayItem in arr do
    if HasStatus(arrayItem, entity)
      return true
    end
  end

  return false
end

This function should iterate over a given array, and checks if the passed entity has a status that exists within the array.

And CL_HasPactWeapon:

function CL_HasPactWeapon(entity, mainHand)
  local entity or context.Source
  local weaponEntity = (GetActiveWeapon(entity, true))

  local PACT_STATUSES = [
    'PACT_BLADE',
    'CL_PACT_BLADE_STR',
    'CL_PACT_BLADE_DEX',
    'CL_PACT_BLADE_CON',
    'CL_PACT_BLADE_INT',
    'CL_PACT_BLADE_WIS'
  ]

  if weaponEntity.IsValid then

    result = Character(entity) & CL_HasStatusFromArray(weaponEntity)
    return ConditionResult(result.Result, {ConditionError("HasNotPactWeapon")}, {ConditionError("HasPactWeapon")})
  end
  return ConditionResult(false, {ConditionError("HasNotPactWeapon")}, {ConditionError("HasPactWeapon")})
end

This function is almost identical to the original HasPactWeapon() spell, with two exceptions:

  1. It defines an array of all the Pact Blade statuses.
  2. It checks HasStatusFromArray() rather than manually checking each status

To implement it, you would want to do the following:

new entry "ThirstingBlade_Blade"
type "PassiveData"
using "ThirstingBlade_Blade"
data "Conditions" "((not context.HasContextFlag(StatsFunctorContext.OnStatusRemoved) and (HasStringInSpellRoll('WeaponAttack') or HasStringInSpellRoll('UnarmedAttack') or SpellId('Target_CommandersStrike') or SpellId('Projectile_ArrowOfSmokepowder')) and HasUseCosts('ActionPoint', true) and not Tagged('EXTRA_ATTACK_BLOCKED',context.Source) and TurnBased(context.Source)) or (context.HasContextFlag(StatsFunctorContext.OnStatusRemoved) and StatusId('INITIAL_ATTACK_TECHNICAL') and TurnBased())) and CL_HasPactWeapon() and HasPassive('PactOfTheBlade',context.Source)"

This takes the existing ThirstingBlade_Blade, the only place that checks HasPactWeapon, has it inherit its original self, and then changes the Conditions to call CL_HasPactWeapon() instead of the regular HasPactWeapon() function

Ragingg13 commented 11 months ago

While it isn't the same approach I took, based on your additions above this would work as this is the same/similar change that I made to the base function. Conceptually it works :)