Affordability Preview - MSUTeam/MSU GitHub Wiki

Description

Thanks to the flexibility of the base values saving feature in MSU, it is possible to implement skills which incrementally modify the Action Point and Fatigue costs of other skills based on their usage. For example, let's imagine a case where every use of the Thrust skill reduces the Fatigue Cost of the Shieldwall skill by 15% or each tile moved reduces the Action Point cost of the next attack by 1.

This leads to an issue: while the player is previewing a movement or the use of a skill, the UI updates the affordabilitiy preview of other skills based on their current costs and not the cost that will be after completing the previewed action. This can be misleading for the player as a skill which would have been affordable after the previewed action appears as unaffordable until the action is actually taken. MSU solves this problem by allowing you to specify in such skills how they should modify the affordability preview.

preview2 preview3

onAffordablePreview

function onAffordablePreview( _skill, _movementTile )
// _skill is a BB object
// _movementTIle is a C++ tile object

While a player is previewing the use of a skill, the previewed skill will be passed as _skill and _movementTile will be null. While the player is previewing a movement, the furthest possible tile that is affordable to be moved to in the currently attempted movement is passed as _movementTile and _skill will be null.

This function is called for all skills whenever a preview is initiated as long as the ExpandedSkillTooltips MSU Setting is enabled. You can use this function to modify the affordability preview of skills.

modifyPreviewField

this.modifyPreviewField( _skill, _field, _newChange, _multiplicative )
// _skill is the skill whose preview affordability is to be changed
// _field is a string that is a key in the m table of _skill
// _newChange should be an integer, float or boolean
// _multiplicative is a boolean

This function can be used during onAffordablePreview to modify the value of _field in the m table of _skill for the preview affordability of _skill. It is entirely the modder's responsibility to provide the correctly calculated values for _newChange. If _multiplicative is true then the changes are applied multiplicatively, otherwise additively. See Example.

modifyPreviewProperty

this.modifyPreviewProperty( _skill, _field, _newChange, _multiplicative )
// _skill is the skill whose preview affordability is to be changed
// _field is a string that is a key in ::Const.CharacterProperties
// _newChange should be an integer, float or boolean
// _multiplicative is a boolean

This function can be used during onAffordablePreview to modify the value of _field in the CurrentProperties of the character for the preview affordability of _skill. It is entirely the modder's responsibility to provide the correctly calculated values for _newChange. If _multiplicative is true then the changes are applied multiplicatively, otherwise additively. See Example.

addPreviewApplicableFunction

::MSU.Skills.addPreviewApplicableFunction( _name )
// _name is a string which is the name of a function in skill.nut

Makes this function subject to MSU's affordability preview modifications. This is essential, for example, for mods which may want to add new kinds of affordability calculation functions e.g. <skill>.isAffordableBasedOnMana() etc.

Example

Simple Example

Let's say we have a perk called MSU-Shot which reduces the Action Point cost of Aimed Shot by 1 every time it is used. We can accomplish this as follows:

// MSU-Shot modifies the ActionPointCost of Aimed Shot based on how many times it has been used
function onAfterUpdate( _properties )
{
    local aimedShot = this.getContainer().getSkillById("actives.aimed_shot");
    if (aimedShot != null)
    {
        aimedShot.m.ActionPointCost -= this.m.UseCount;
    }
}

// Now we do the same thing in onAffordablePreview of MSU-Shot to modify the preview cost of Aimed Shot
function onAffordablePreview( _skill, _movementTile )
{
    if (_skill != null && _skill == this) // i.e. the player is previewing the use of MSU-Shot
    {
        local aimedShot = this.getContainer().getSkillById("actives.aimed_shot");
        if (aimedShot != null)
        {
            // set the preview to reduce the ActionPointCost based on current use count + 1
            this.modifyPreviewField(aimedShot, "ActionPointCost", -(this.m.UseCount + 1), false);
        }
    }
}

Complex Example

Let's say we have a perk called MSU-Assault which reduces the Action Point cost of every melee attack skill by 1 and Fatigue cost by 10% for every 2 tiles moved. However, upon using a skill, any current bonus should be reset. In order to apply these changes to the affordability preview of such skills, we write the following code in the MSU-Assault script:

// MSU-Assault modifies the ActionPointCost and FatigueCostMult of certain skills in its onAfterUpdate function
function onAfterUpdate( _properties )
{
    local bonus = this.getContainer().getActor().getTile().getDistanceTo(this.m.StartingTile) / 2;
    foreach (skill in this.getContainer().getSkillsByFunction(@(skill) skill.isAttack() && !skill.isRanged()))
    {
        skill.m.ActionPointCost += -bonus;
        skill.m.FatigueCostMult *= 1.0 - bonus * 0.1;
    }
}

// Then we simply do the same thing in the onAffordablePreview function of MSU-Assault but use the predicted bonus
function onAffordablePreview ( _skill, _movementTile )
{
    // If the character is previewing a movement, apply the predicted bonus to the preview
    if (_movementTile != null)
    {
        local bonus = _movementTile.getDistanceTo(this.m.StartingTile) / 2;

        foreach (skill in this.getContainer().getSkillsByFunction(@(skill) skill.isAttack() && !skill.isRanged()))
        {
            this.modifyPreviewField(skill, "ActionPointCost", -bonus, false);
            this.modifyPreviewField(skill, "FatigueCostMult", 1 - bonus * 0.1, true);
        }
    }

    // If the character is previewing a skill usage, reset any current bonus
    if (_skill != null)
    {
        foreach (skill in this.getContainer().getSkillsByFunction(@(skill) skill.isAttack() && !skill.isRanged()))
        {
            this.modifyPreviewField(skill, "ActionPointCost", 0, false);
            this.modifyPreviewField(skill, "FatigueCostMult", 1, true);
        }
    }
}

previewcost

⚠️ **GitHub.com Fallback** ⚠️