A healer who can only heal 20 hit points, total

The place to post your WML questions and answers.

Moderator: Forum Moderators

Forum rules
  • Please use [code] BBCode tags in your posts for embedding WML snippets.
  • To keep your code readable so that others can easily help you, make sure to indent it following our conventions.
Post Reply
User avatar
Helmet
Posts: 138
Joined: December 19th, 2006, 5:28 pm
Location: Florida, USA

A healer who can only heal 20 hit points, total

Post by Helmet »

I have a custom healer-type unit who uses potions. He heals the usual 4 hit points of damage. I want to weaken this healer a little, but in an interesting way. Specifically, I want to limit him to healing 20 hit points of damage, total.

When the healer reaches his maximum points of damage healed (20 hit points), I want his healing ability to stop; he is out of potions.

I'm also thinking the healer would restock his supply of healing potions whenever he enters a village. This would reset the number of hit points he had healed to 0.

What do you think? Should we see his healing animation and a red 0 float up when he heals nothing? Or should nothing happen at all? Probably nothing happening is better.

What code would be needed to accomplish this? Thanks for your help.
User avatar
WhiteWolf
Forum Moderator
Posts: 654
Joined: September 22nd, 2009, 7:48 pm
Location: Hungary

Re: A healer who can only heal 20 hit points, total

Post by WhiteWolf »

Interesting, I really like this concept.
Maybe when the 0-healing happens for the first time, it's useful to see that "oh right he's out of ammo, needs resupplying", but... if there are no means to do that within a few turns, it might get repetitive and annoying, having to see that floating 0 at start of each turn. So I'd vote for nothing to happen instead.

The way I'd do this, is to count the number of its remaining potions in the unit's variables. That way, you can give in its [heals] ability a filter for:

Code: Select all

[filter_self]
    formula = self.variables.potions > 0
[/filter_self]
I wrote this from memory, but it should work, and it should automate the ability based on this variable.

As far as I know, there is no "on heal" event, so tracking the variable is a bit tricky, but here's how I'd do it:
Write a "side turn" event, that stores all such healers and adjacent allied units - if they are harmed, make a record, that this unit is going to heal.
Then use a "turn refresh" event, which takes place after the healing is done, to sub the recorded value from the potions variable.

You'll then need another "turn refresh" event the refreshes the potions variable for such units that stand in villages.
Author of the Underness Series, consisting of 5 parts: The Desolation of Karlag, The Blind Sentinel, The Stone of the North, The Invasion Of The Western Cavalry, Fingerbone of Destiny
Standalone works: The Ravagers - now for 1.14, with new bugs!
User avatar
Celtic_Minstrel
Developer
Posts: 1691
Joined: August 3rd, 2012, 11:26 pm
Location: Canada
Contact:

Re: A healer who can only heal 20 hit points, total

Post by Celtic_Minstrel »

WhiteWolf wrote: October 17th, 2020, 7:46 pm

Code: Select all

[filter_self]
    formula = self.variables.potions > 0
[/filter_self]
Formulas operate on a completely different view of the unit. It's similar to the WML view of a unit but not quite the same, and this is one of the places where it differs. In a formula, units have two types of variables — formula variables, and WML variables. Assuming you want to store the potions as a WML variable, it'll look more like this:

Code: Select all

[filter_self]
    formula = wml_vars.potions > 0
[/filter_self]
EDIT: Also, just to be clear, "self.x" is the same as writing just "x", so the above is the same as "self.wml_vars.potions"
Last edited by Celtic_Minstrel on October 18th, 2020, 3:51 am, edited 1 time in total.
Author of The Black Cross of Aleron campaign and Default++ era.
Maintainer of Steelhive.
User avatar
WhiteWolf
Forum Moderator
Posts: 654
Joined: September 22nd, 2009, 7:48 pm
Location: Hungary

Re: A healer who can only heal 20 hit points, total

Post by WhiteWolf »

I see, I stand corrected :)
Author of the Underness Series, consisting of 5 parts: The Desolation of Karlag, The Blind Sentinel, The Stone of the North, The Invasion Of The Western Cavalry, Fingerbone of Destiny
Standalone works: The Ravagers - now for 1.14, with new bugs!
User avatar
Helmet
Posts: 138
Joined: December 19th, 2006, 5:28 pm
Location: Florida, USA

Re: A healer who can only heal 20 hit points, total

Post by Helmet »

WhiteWolf wrote: October 17th, 2020, 7:46 pm Interesting, I really like this concept.
Thanks!
WhiteWolf wrote: October 17th, 2020, 7:46 pm Write a "side turn" event, that stores all such healers and adjacent allied units - if they are harmed, make a record, that this unit is going to heal.
Then use a "turn refresh" event, which takes place after the healing is done, to sub the recorded value from the potions variable.

You'll then need another "turn refresh" event the refreshes the potions variable for such units that stand in villages.
Hmm. That makes sense, but I don't think I can do that. Adjacent units and the storing and comparing of values aren't things I can do just yet. Maybe I could do a side turn event, but the filter might not work. I'm pretty sure the coding is beyond the abilities of the person I usually ask for help, too.

If any WML wizard on this forum would whip this out, I would appreciate it. No need to test it, just give it your best shot, first draft. Then at least I'd have something to study, test, and debug.
User avatar
WhiteWolf
Forum Moderator
Posts: 654
Joined: September 22nd, 2009, 7:48 pm
Location: Hungary

Re: A healer who can only heal 20 hit points, total

Post by WhiteWolf »

Well, I'm still writing this from memory without testing anything. I'm sure this could be simplified and further optimized, but it should serve as a draft. Also, please excuse me for using the deprecated format of foreach, that's the one I've got in my pinky, feel free to switch it for the more robust [foreach] syntax.

This will record the potion units that are about to heal:

Code: Select all

[event]
    name=side turn
    first_time_only=no
    id=record_healers
    
    # find potion units with >0 number of potions and harmed allies nearby
    [store_unit]
        [filter]
            type=Potion Giver Unit Type
            side=$side_number
            formula= wml_vars.potions > 0
            [filter_adjacent]
                is_enemy=no
                adjacent=n,ne,se,s,sw,nw
                count=1-6
                formula = $other_unit.hitpoints <$other_unit.max_hitpoints # I'm not sure if this is correct, I may be mixing up how formula variables are handled again. 
                # Someone should doublecheck if this works like this.
            [/filter_adjacent]
        [/filter]
        variable=active_healers
        kill=no
    [/store_unit]
    
    # for each potioner, count how many adjacent allies they will heal, match this info to the healer and save it in an array
    {FOREACH active_healers i}
        [store_unit]
            [filter]
                x,y=$active_healers[$i].x,$active_healers[$i].y
                radius=1
                [filter_side]
                    [allied_with]
                        side=$side_number
                    [/allied_width]
                [/filter_side]
                formula= hitpoints < max_hitpoints
            [/filter]
            variable=healed_by_healer_i
            kill=no
        [/store_unit]
        
        [set_variables]
            name=healer_record
            mode=append
            [value]
                id=$active_healers.id
            [/value]
            [value]
               number_healed=$healed_by_healer_i.length
            [/value]
        [/set_variables]
    {NEXT i}
    {CLEAR_VARIABLE active_healers,healed_by_healer_i}
[/event]
After healing done, sub the potions, and refresh if in a village. If you want to be more concise in the code, you can probably replace the [store_unit]->{VARIABLE_OP}->[unstore_unit] chains here with a single [modify_unit] call, where the new value to be set can be expressed with a fancy implicit formula call: VAR = "$($array[$i].variables.VAR +/- value)".

Code: Select all

# sub potions from units that healed
[event]
    name=turn refresh
    first_time_only=no
    id=sub_potions
    
    # iterate over the recorded variable
    {FOREACH healer_record i}
        [store_unit]
            [filter]
                id=$healer_record[$i].id
            [/filter]
            variable=sub_poti
            kill=yes
        [/store_unit]
        {VARIABLE_OP sub_poti.variables.potions_number sub $healer_record[$i].number_healed}
        [unstore_unit]
            variable=sub_poti
            find_vacant=no
        [/unstore_unit]
    {NEXT i}
    {CLEAR_VARIABLE sub_poti,healer_record}
[/event]

# refresh potions for those in villages
[event]
    name=turn refresh
    first_time_only=no
    id=refresh_potions

    # store healers standing in villages
    [store_unit]
        [filter]
            type=Potion Giver Unit Type
            side=$side_number
            [filter_location]
                terrain=*^V*
            [/filter_location]
        [/filter]
        variable=refresh
        kill=no
    [/store_unit]
    
    {FOREACH refresh i}
        {VARIABLE_OP refresh[$i].variables.potion_number add X} # where X is how much you want to replenish their stock with. 
        
        # if we exceed 20, just cut off and set it to 20:
        [if]
            [variable]
                name=refresh[$i].variables.potion_number
                greater_than=20
            [/variable]
            [then]
                {VARIABLE refresh[$i].variables.potion_number 20}
            [/then]
        [/if]
        
        [unstore_unit]
            variable=refresh[$i]
            find_vacant=no
        [/unstore_unit]
    {NEXT i}
    {CLEAR_VARIABLE refresh}
[/event]


There is one bug in this already, and I don't yet know how to solve it and will require you to think a lot about it - currently if a unit has 2 potions, but has 4 harmed allies nearby, it will still heal all 4 of them, and their supply will then drop to -2. You could implement a threshold in the code so that if it goes to negative it just sets to 0, but it's far from trivial (if possible at all) to solve the overhealing.
Also, if 3 units stand next to each other in this order: healer, harmed, healer, but just a single heal will heal the harmed unit to max HP, this code still counts this as if both healers had to heal the harmed unit.
These are inevitable, because we don't have a way (that I know of) to manipulate who's getting healed exactly, we can only have events pre and post the healing process.
Author of the Underness Series, consisting of 5 parts: The Desolation of Karlag, The Blind Sentinel, The Stone of the North, The Invasion Of The Western Cavalry, Fingerbone of Destiny
Standalone works: The Ravagers - now for 1.14, with new bugs!
User avatar
Helmet
Posts: 138
Joined: December 19th, 2006, 5:28 pm
Location: Florida, USA

Re: A healer who can only heal 20 hit points, total

Post by Helmet »

Wow, thanks White Wolf.

All the maps in my campaign are small, and the number of recruits is small. Maybe I could get by with one healer, and thus eliminate the undesirable behavior that happens when two healers stand near the same wounded unit.

Hmm. Thanks again!
User avatar
Celtic_Minstrel
Developer
Posts: 1691
Joined: August 3rd, 2012, 11:26 pm
Location: Canada
Contact:

Re: A healer who can only heal 20 hit points, total

Post by Celtic_Minstrel »

I guess your other option would be to add a dummy ability instead of a heal ability and manually apply the healing in a turn refresh event (using the [heal_unit] ActionWML tag). That would allow you to get the exact behaviour you want but would be more work on your part.
Author of The Black Cross of Aleron campaign and Default++ era.
Maintainer of Steelhive.
Post Reply