- Joined
- Jul 30, 2012
- Messages
- 156
Heroic Unit - Units with hero capabilities
My main motivation to find this is because I'm developing a system, that makes heavy use of Engineering Upgrade. And as you all know, you cannot add Engineering Upgrade to non-hero units. If you do, it crashes the game.
I tracked down the cause of this crash: It's because Enginnering Upgrade tries to access the 'AHer' ability of a unit. Since non-heroes don't have this ability, CUnit::GetAbility returns null, and the game crashes with a null-pointer exception.
So I started to look for a way to add 'AHer' to non-hero units, and I finally got it! To do that, I morph the unit into a hero temporarily, using a powerup. As we know, this kind of morphing requires some cautions:
Comments on the hero unit type: Blizzard did not account for morphing units into heroes/backwards. Heroes benefit from hero attributes, which influence stats such as hitpoints or armor. When the unit reverts into non-hero form, those bonuses are not dissolved, so the unit ends up with false values. To counter this, just set hero attributes to zero. Also set the base armor value of heroes to 0 (gameplay constant Hero Attribute - Defense Base (before Agility Bonus)). You can equalize this by manually adding the default base armor to each hero in your map. In addition, check Values - Hero - Hide Hero Interface Icon, else the hero portrait will persist.
My system takes care of almost all of them, the only you thing you need to do is to change the Gameplay Constant above to 0.
How it works
Basically, when you morph a non-hero to a hero, 3 things happen, in this sequence:
1 - Hero attributes are applied. The unit gains the 'AHer' ability.
2 - The unit loses all non-permanent abilities and gains the abilities of the new unit type.
3 - The stats of the new unit type (such as life and movement speed) are applied.
The trick is, by using a life change event, I can intercept the morphing function in the first step, and make the unit morph back immediately. After that, the previous morphing function will be resumed, and the 'AHer' ability will be added to our unit in the non-hero form.
Adding 'AHer' to a non-hero unit causes many interesting things:
- The unit can have Hero Skills!!! After calling the function, the unit will have the Hero Skills of the hero type used to morph temporarily.
- The unit will benefit from Hero Attributes, you can and or remove abilities that modify hero stats, and they will work as expected.
- The unit can make full use of Engineering Upgrade and Tome items, without crashing
- The unit can acquire experience and even level up! Leveling up will add hero stats as they were specified in the temporary hero type.
- The unit still behaves as non-hero for spell targeting rules, and it will normally decay and be removed of the game after death
- Unfortunately some (if not all) of the JASS natives for heroes (SetHeroLevel, SetHeroStr/Agi/Int) will NOT work for this "Heroic Unit". But you can still manipulate the unit's stats and level by using Attribute Bonus and Tome of Power.
How to use it
Jass NewGen Pack is required ofc. You just need to create a trigger, convert to custom text, and add this library as the code. Pay attention to the IDs that I'm using on the textmacro. You may need to modify them to avoid conflicts with other things on your map.
JASS:
//! zinc
library HeroicUnit
{
//! textmacro CreateObjects takes HERO, SPELL, ITEM
//! external ObjectMerger w3u nech $HERO$ uabi "AInv" uhhb 1 ustr 1
//! external ObjectMerger w3a Amrf $SPELL$ Emeu 1 "$HERO$"
//! external ObjectMerger w3t tkno $ITEM$ iabi "$SPELL$"
constant integer HERO_ID = '$HERO$'; //Hero type into which the unit morphs.
constant integer SPELL_ID = '$SPELL$'; //Spell that morphs the unit into a hero temporarily.
constant integer ITEM_ID = '$ITEM$'; //Powerup that holds the morphing spell
constant integer BONUS_ID = 'AIs1'; //Stat bonus ability. Must provide exactly the same stats of the hero.
constant integer DETECTOR = 'Adef'; //Used to detect morphing. Immolation could be used too
constant integer ORDER = 852056; //Order Id for "undefend". Should be 852178 for "unimmolation"
//! endtextmacro
//! runtextmacro CreateObjects("N000", "A000", "I000")
function OnMorph() -> boolean
{
unit u = GetTriggerUnit();
trigger t = GetTriggeringTrigger();
if (GetTriggerEventId() == EVENT_UNIT_STATE_LIMIT)
{
DisableTrigger(t);
UnitRemoveAbility(u, SPELL_ID);
}
else if (GetUnitTypeId(u) != HERO_ID)
{
UnitAddAbility(u, SPELL_ID);
UnitAddAbility(u, BONUS_ID);
UnitMakeAbilityPermanent(u, true, BONUS_ID);
TriggerRegisterUnitStateEvent(t, u, UNIT_STATE_LIFE, GREATER_THAN, GetWidgetLife(u)+1.);
RemoveItem(UnitAddItemById(u, ITEM_ID));
}
else
{
UnitAddAbility(u, DETECTOR);
}
t = null;
u = null;
return false;
}
public function UnitMakeHeroic (unit u) -> boolean
{
trigger t = CreateTrigger();
real hp = GetWidgetLife(u);
real mp = GetUnitState(u, UNIT_STATE_MANA);
SetWidgetLife(u, GetUnitState(u, UNIT_STATE_MAX_LIFE));
TriggerRegisterUnitEvent(t, u, EVENT_UNIT_ISSUED_ORDER);
TriggerAddCondition(t, Condition(function OnMorph));
UnitAddAbility(u, 'AInv');
UnitAddAbility(u, DETECTOR);
UnitAddAbility(u, BONUS_ID);
RemoveItem(UnitAddItemById(u, ITEM_ID));
UnitRemoveAbility(u, BONUS_ID);
SetWidgetLife(u, hp);
SetUnitState(u, UNIT_STATE_MANA, mp);
SetUnitAnimation(u, "stand");
DestroyTrigger(t);
t = null;
return GetUnitAbilityLevel(u, 'AHer') > 0;
}
}
//! endzinc
Then, when you want to make a unit "heroic", just call UnitMakeHeroic, and the 'AHer' ability will be magically added to your unit, giving all the effects stated above. Note that this function will not interrupt the unit's orders in any way, but it will reset the unit's current animation.
I just finished this snippet yesterday, there are still many things that must be done, such as a good tutorial and proper documentation, as well as improvements to the code. So I can say this is still a "beta" version, but it's already stable enough to be used.
I appreciate any feedback, as well as suggestions for improvement, this is my first "Library", if there are any programming conventions that I failed to follow, I'd like to know.
EDIT: Now I have attached a TestMap. In this map, the Goblin Tinker is changed to a normal unit, and he can still learn all of his hero skills, even Engineering Upgrade. Also you will note that his stats increase when leveling up.
Attachments
Last edited: