1 (edited by Meractus 2016-10-02 16:36:11)

Topic: [Tutorial] Health Drain Debuff

Hello Everyone!

This is an adaptation and expansion of the Gauge Drain. I created it to have a "bleed" effect/debuff on characters.

This was made on UFE 1.8 and unity 5.4.1f1.

For this to work you will need to change CharacterInfo.cs, MoveInfo.cs, MoveEditorWindow.cs and ControlsScript.cs.

In CharacterInfo.cs, right before:

[System.Serializable]
public class CharacterInfo: ScriptableObject

Add:

[System.Serializable]
public class CharacterDebuff {
    public string moveName;
    public int playerNumber;
    public float hpDrainDuration;
    public float currentHPDrainDuration;
    public float hpDrainInterval;
    public float currentHPDrainInterval;
    public float damagePerInterval;
    public MoveInfo hpDrainMove;
    public CombatStances hpDrainStance;
}

That's all for CharacterInfo.

==================================================================================

In MoveInfo.cs, after:

public enum CurrentFrameData {
    Any,
    StartupFrames,
    ActiveFrames,
    RecoveryFrames
}

Add:

public enum HealthDrainStart {
    OnMoveCast,
    OnHit
}

And after:

public CombatStances DCStance;

Add:

    // Health drain variables
    public bool isSelfHPDrain;
    public HealthDrainStart selfDrainMode;
    public float selfHPDrainDuration;
    public float selfHPDrainInterval;
    public float selfDamagePerInterval;
    public bool stopDrainingOwnHealth; //Clears all health drains on self that were inflicted by yourself
    public MoveInfo selfHPDrainMove;
    public CombatStances selfHPDrainStance;

    public bool isOpHPDrain;
    public HealthDrainStart opDrainMode;
    public float opHPDrainDuration;
    public float opHPDrainInterval;
    public float opDamagePerInterval;
    public bool stopOpHealthDrainFromSelf; //Clears all health drains on self that were inflicted by the opponent

That's it for MoveInfo.

==================================================================================

In MoveEditorWindow.cs, after:

moveInfo.stopDrainingGauge = EditorGUILayout.Toggle("Stop Draining", moveInfo.stopDrainingGauge, toggleStyle);

EDIT: The original is above. Change it to the lines below for more clarity.

moveInfo.stopDrainingGauge = EditorGUILayout.Toggle("Stop Draining Gauge" , moveInfo.stopDrainingGauge , toggleStyle);
EditorGUILayout.Space();

Add:

// Health Drain: Self
EditorGUI.BeginDisabledGroup(moveInfo.stopDrainingOwnHealth); {
    moveInfo.isSelfHPDrain = EditorGUILayout.Toggle("Drain Own Health" , moveInfo.isSelfHPDrain , toggleStyle);
    if (moveInfo.isSelfHPDrain) {
        moveInfo.selfDrainMode = (HealthDrainStart)EditorGUILayout.EnumPopup("Drain Starts On:" , moveInfo.selfDrainMode);
        moveInfo.selfHPDrainDuration = EditorGUILayout.FloatField("- Drain Duration (seconds):" , moveInfo.selfHPDrainDuration);
        moveInfo.selfHPDrainInterval = EditorGUILayout.FloatField("- Drain Interval (seconds):" , moveInfo.selfHPDrainInterval);
        moveInfo.selfDamagePerInterval = EditorGUILayout.FloatField("- Damage Per Interval:" , moveInfo.selfDamagePerInterval);
        moveInfo.selfHPDrainMove = (MoveInfo)EditorGUILayout.ObjectField("- Move (Drain Complete):" , moveInfo.selfHPDrainMove , typeof(MoveInfo) , false);
        moveInfo.selfHPDrainStance = (CombatStances)EditorGUILayout.EnumPopup("- Stance (Drain Complete):" , moveInfo.selfHPDrainStance , enumStyle);
    }
}
EditorGUI.EndDisabledGroup();

moveInfo.stopDrainingOwnHealth = EditorGUILayout.Toggle("Stop Draining Own Health" , moveInfo.stopDrainingOwnHealth , toggleStyle);
EditorGUIUtility.labelWidth = 300;
moveInfo.stopOpHealthDrainFromSelf = EditorGUILayout.Toggle("Stop Opponent's Health Drain On Yourself" , moveInfo.stopOpHealthDrainFromSelf , toggleStyle);
EditorGUIUtility.labelWidth = 150;

After:

moveInfo.opGaugeGainOnParry = StyledSlider("Gauge Gain on Parry (%)" , moveInfo.opGaugeGainOnParry , EditorGUI.indentLevel , 0 , 100);

Add:

EditorGUIUtility.labelWidth = 200;
// Health Drain: Opponent
moveInfo.isOpHPDrain = EditorGUILayout.Toggle("Drain Opponent's Health" , moveInfo.isOpHPDrain , toggleStyle);
if (moveInfo.isOpHPDrain) {
    moveInfo.opDrainMode = (HealthDrainStart)EditorGUILayout.EnumPopup("Drain Starts On:" , moveInfo.opDrainMode);
    moveInfo.opHPDrainDuration = EditorGUILayout.FloatField("- Drain Duration (seconds):" , moveInfo.opHPDrainDuration);
    moveInfo.opHPDrainInterval = EditorGUILayout.FloatField("- Drain Interval (seconds):" , moveInfo.opHPDrainInterval);
    moveInfo.opDamagePerInterval = EditorGUILayout.FloatField("- Damage Per Interval:" , moveInfo.opDamagePerInterval);
}

That's all for MoveEditorWindow.

==================================================================================

In ControlsScript.cs, in the variables declaration at the beggining of the script, after:

private CombatStances DCStance;

Add:

// Keep Track of all the health drains on the player
private List<CharacterDebuff> playerDebuffs;

Inside the Start() function, after:

if (playerNum == 2) UFE.FireGameBegins();

Add:

// Health Drain
DrainHealth(true);

Inside DoFixedUpdate(), after:

if (currentMove != null) ReadMove(currentMove);

Add:

// Health Drain
DrainHealth(false);

Inside ReadMove(), inside the if:

if (move.currentTick == 0)

After:

if (move.stopDrainingGauge) {
    gaugeDPS = 0;
    inhibitGainWhileDraining = false;
}

Add:

// Health drain
// Self Drain
if (move.isSelfHPDrain && move.selfDrainMode == HealthDrainStart.OnMoveCast) {
    StartHealthDrain(move , this.playerNum);
}

// Opponent Drain
if (move.isOpHPDrain && move.opDrainMode == HealthDrainStart.OnMoveCast) {
    opControlsScript.StartHealthDrain(move , this.playerNum);
}

// Stop Health Drain
StopHealthDrain(move);

Inside GetHit(), as the first thing, add:

// Health Drain
if (opControlsScript.currentMove != null) {
    if (opControlsScript.currentMove.isSelfHPDrain && opControlsScript.currentMove.selfDrainMode == HealthDrainStart.OnHit) {
        opControlsScript.StartHealthDrain(opControlsScript.currentMove , opControlsScript.playerNum);
    }
    if (opControlsScript.currentMove.isOpHPDrain && opControlsScript.currentMove.opDrainMode == HealthDrainStart.OnHit) {
        StartHealthDrain(opControlsScript.currentMove , opControlsScript.playerNum);
    }
}

Do the same for GetHitBlocking(), if you want this to work even when blocking.

Inside EndRound(), after:

opControlsScript.ResetDrainStatus(true);

Add:

// Reset Health Drain
DrainHealth(true);
opControlsScript.DrainHealth(true);

Now, at the very end of ControlsScript, after:

void shake() {
    float rnd = Random.Range(-.1f * shakeDensity , .2f * shakeDensity);
    character.transform.localPosition = new Vector3(rnd , 0 , 0);
}

Add the functions that will make it all work:

// Start health drain
public void StartHealthDrain(MoveInfo inputMove , int inputPlayer) {
    bool foundExistingSelfDebuff = false;
    bool foundExistingOpDebuff = false;
    foreach (CharacterDebuff debuff in playerDebuffs) {
        // Drain Opponent's Health
        if (debuff.moveName == inputMove.moveName &&
            inputMove.isOpHPDrain &&
            inputPlayer != this.playerNum) {
            foundExistingOpDebuff = true;
            debuff.hpDrainDuration = inputMove.opHPDrainDuration;
            debuff.hpDrainInterval = inputMove.opHPDrainInterval;
            debuff.damagePerInterval = inputMove.opDamagePerInterval;
            debuff.currentHPDrainDuration = 0f;
            debuff.currentHPDrainInterval = 0f;
        }
        // Drain Own Health
        if (debuff.moveName == inputMove.moveName &&
            inputMove.isSelfHPDrain &&
            inputPlayer == this.playerNum) {
            foundExistingSelfDebuff = true;
            debuff.hpDrainDuration = inputMove.selfHPDrainDuration;
            debuff.hpDrainInterval = inputMove.selfHPDrainInterval;
            debuff.damagePerInterval = inputMove.selfDamagePerInterval;
            debuff.currentHPDrainDuration = 0f;
            debuff.currentHPDrainInterval = 0f;
        }
    }
    // New Drain: opponent's health
    if (!foundExistingOpDebuff && inputMove.isOpHPDrain && inputPlayer != this.playerNum) {
        if (playerDebuffs == null) playerDebuffs = new List<CharacterDebuff>();
        CharacterDebuff newDebuff = new CharacterDebuff();
        newDebuff.moveName = inputMove.moveName;
        newDebuff.playerNumber = inputPlayer;
        newDebuff.hpDrainDuration = inputMove.opHPDrainDuration;
        newDebuff.hpDrainInterval = inputMove.opHPDrainInterval;
        newDebuff.damagePerInterval = inputMove.opDamagePerInterval;
        newDebuff.currentHPDrainDuration = 0f;
        newDebuff.currentHPDrainInterval = 0f;
        playerDebuffs.Add(newDebuff);
    }

    // New Drain: own health
    if (!foundExistingSelfDebuff && inputMove.isSelfHPDrain && inputPlayer == this.playerNum) {
        if (playerDebuffs == null) playerDebuffs = new List<CharacterDebuff>();
        CharacterDebuff newDebuff = new CharacterDebuff();
        newDebuff.moveName = inputMove.moveName;
        newDebuff.playerNumber = inputPlayer;
        newDebuff.hpDrainDuration = inputMove.selfHPDrainDuration;
        newDebuff.hpDrainInterval = inputMove.selfHPDrainInterval;
        newDebuff.damagePerInterval = inputMove.selfDamagePerInterval;
        newDebuff.currentHPDrainDuration = 0f;
        newDebuff.currentHPDrainInterval = 0f;
        newDebuff.hpDrainMove = inputMove.selfHPDrainMove;
        newDebuff.hpDrainStance = inputMove.selfHPDrainStance;
        playerDebuffs.Add(newDebuff);
    }
}

// Stop Health Drain
public void StopHealthDrain(MoveInfo inputMove)
{
    foreach (CharacterDebuff debuff in playerDebuffs) {
        // Self debuffs
        if (inputMove.stopDrainingOwnHealth && debuff.playerNumber == this.playerNum) {
            debuff.currentHPDrainDuration = debuff.hpDrainDuration;
        }
        // Opponent's debuffs on self
        if (inputMove.stopOpHealthDrainFromSelf && debuff.playerNumber != this.playerNum) {
            debuff.currentHPDrainDuration = debuff.hpDrainDuration;
        }
    }
}

// Health drain
public void DrainHealth(bool resetDrain)
{
    if (!resetDrain) {
        foreach (CharacterDebuff debuff in playerDebuffs) {
            if (debuff.currentHPDrainDuration < debuff.hpDrainDuration) {
                if (debuff.currentHPDrainInterval < debuff.hpDrainInterval) {
                    debuff.currentHPDrainInterval += Time.fixedDeltaTime;
                }
                else {
                    DamageMe(debuff.damagePerInterval);
                    debuff.currentHPDrainInterval = 0f;
                }
                debuff.currentHPDrainDuration += Time.fixedDeltaTime;
                // Cast a move and/or change stance
                if (debuff.playerNumber == this.playerNum && debuff.currentHPDrainDuration >= debuff.hpDrainDuration) {
                    myMoveSetScript.ChangeMoveStances(debuff.hpDrainStance);
                    if (debuff.hpDrainMove != null) CastMove(debuff.hpDrainMove , true);
                }
            }
        }
    }
    else {
        // Starts/Resets the variable
        playerDebuffs = new List<CharacterDebuff>();
    }
}

EDIT 2: Adding Some usage example.

http://i63.tinypic.com/2crt3qb.jpg

Drain Starts On: Will trigger the drain either on move cast or when it hits the opponent.
Drain Duration: Total Duration of the drain.
Drain Interval: Damage will be applied every time it reaches this amount of time.
Damage per Interval: Damage applied at each interval.
Move : When the drain ends, will cast this move.
Stance: When the drain ends, will change to this stance.

Stop Draining Own Health: When this is marked, the move will stop any drain on yourself that was cast by you.
Stop Opponent's Health Drain on Yourself: When this is marked, the move will stop any drain on yourself that was cast by teh opponent.

The ones listed above will drain your own Health, set negative values to heal yourself.
There are options to drain the opponent's health as well, the variables that repeat have the same function as the Drain Own Health options.

Special attention to this:
Just like in the version for 1.6 I haven't thought in a way to make the intervals to work exactly as I want.
In the example given in the image, the Drain will last for 10 seconds and will apply damage each second(Interval).
The problem is that it will only trigger 9 times since when the timer "reaches" 10 seconds, it will be like 10.167 seconds and will not enter the conditions I set. To work around this, just set the duration a little higher than the intended (in the example given, 10.2 will work, since 10.2 is higher than 10.167).

That's it guys, let me know if something is not clear or if maybe I have forgotten anything.

Share

Thumbs up +4 Thumbs down

Re: [Tutorial] Health Drain Debuff

I have implemented the code with no errors when compling but the health bar doesnt drain

Share

Thumbs up Thumbs down

Re: [Tutorial] Health Drain Debuff

shannonrattler33 wrote:

I have implemented the code with no errors when compling but the health bar doesnt drain

After implementing it in a fresh project, it worked normally.

Did you remember to set the options in the MoveEditor>Gauge/Meter Options?

Share

Thumbs up +1 Thumbs down

Re: [Tutorial] Health Drain Debuff

Oh ok let me see I don't think I did

Share

Thumbs up Thumbs down

Re: [Tutorial] Health Drain Debuff

Updated the post to add a usage example to help people use this.

Share

Thumbs up +1 Thumbs down