Interkarma / daggerfall-unity

Open source recreation of Daggerfall in the Unity engine
http://www.dfworkshop.net
MIT License
2.7k stars 327 forks source link

Changing Vitals while HUDVitals is not Active #2593

Closed Jagget closed 3 months ago

Jagget commented 6 months ago

Calling

GameManager.Instance.PlayerEntity.CurrentHealth = 32;

When the UI is not in the main HUD status, it does not change the HUD vitals indicator.

Steps to reproduce:

Jagget commented 6 months ago

Action example:

public class ReducePlayerHealth : ActionTemplate
{
    private int _percent;
    private int _amount;

    public ReducePlayerHealth(Quest parentQuest) : base(parentQuest)
    {
    }

    public override string Pattern => @"reduce player health by (?<percent>\d+)|reduce player health on (?<amount>\d+)";

    public override IQuestAction CreateNew(string source, Quest parentQuest)
    {
        Match match = Test(source);

        if (!match.Success) return null;

        var percentGroup = match.Groups["percent"];
        var amountGroup = match.Groups["amount"];

        return new ReducePlayerHealth(parentQuest)
        {
            _percent = percentGroup.Success ? Parser.ParseInt(match.Groups["percent"].Value) : 0,
            _amount = amountGroup.Success ? Parser.ParseInt(match.Groups["amount"].Value) : 0,
        };
    }

    public override void Update(Task _)
    {
        PlayerEntity player = GameManager.Instance.PlayerEntity;

        var health = 0;
        if (_percent > 0)
            health = (int)(player.CurrentHealth - player.MaxHealth / 100f * _percent);

        if (_amount > 0)
            health = player.CurrentHealth - _amount;

        if (health < 1) health = 1;

        player.CurrentHealth = health;
        SetComplete();
    }

    public override object GetSaveData()
    {
        return new SaveData_v1
        {
            percent = _percent,
            amount = _amount,
        };
    }

    public override void RestoreSaveData(object dataIn)
    {
        if (dataIn == null) return;

        var data = (SaveData_v1)dataIn;

        _percent = data.percent;
        _amount = data.amount;
    }

    [FullSerializer.fsObject("v1")]
    private struct SaveData_v1
    {
        public int percent;
        public int amount;
    }
}

Quest example:

Quest: INDAFACE
DisplayName: In da face

QRC:
QuestComplete:  [1004]
I got in da face for money.

QBN:
Item _reward_ gold
start task _damaged_

_damaged_ task:
  reduce player health on 5

_sold_ task:
  when _damaged_
  give pc _reward_
  end quest
John-Leitch commented 6 months ago

Entirely new to unity so forgive any mistakes. Based on my assessment, here's what seems to be occurring:

Between the question completion and the reward, the game is unpaused for a tick. VitalsChangeDetector.UpdateDeltas is called as expected, calculating VitalsChangeDetector.HealthLost from previousHealth and currentHealth, then updating previousHealth. However, because the quest is completed, a DaggerfallUIMessages.dfuiOpenInventoryWindow is posted to DaggerfallUI, pushing a DaggerfallInventoryWindow to the top of the UI. No longer part of the topmost window, the HUDVitals is not updated in that tick. When the inventory is closed and the game is once again unpaused, VitalsChangeDetector.UpdateDeltas doesn't catch the change in health because previousHealth was set the previous tick.

If nobody is averse to the idea, I could contribute a patch for this in the next day or two. It may take a couple rounds of CR as I'm still ramping up on unity and this project.

KABoissonneault commented 3 months ago

Should be fixed in d1d12ff404bc600906cedd25f73b0824843be16b. Thanks for providing a reference script and quest