• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

ArrowKnockback v5

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
  • Like
Reactions: -Berz-
This is an Arrow which moves fast, and units that get hit will get knockbacked and take some Damage.


JASS:
//! zinc
library ArrowKnockback{

    // CHANGEABLE
    // This is the Timer Time
    constant real LoopTime        = 0.0325;
    // This is the AoE where it picks up Units for the Knockback
    constant real PickUpAoE       = 140;
    // This is the Range how much the Knockbacked Units get moved (Multiplied by Level)
    constant real kRange          = 100;
    // This is the Range how mucbh the Arrow will move (Multiplied by Level)
    constant real aRange          = 750;
    // This is the Speed of the Arrow (per Second)
    constant real aSpeed          = 750;
    // This is the Speed of the Knockbacked Units (per Second)
    constant real kSpeed          = 300;
    // This is the Damage a Unit gets when hitted (Multiplied by Level)
    constant real kDamage         = 150;
    // This is the Effect
    constant string speEffect     = "Abilities\\Spells\\Human\\FlakCannons\\FlakTarget.mdl";
    // This is the Spell ID
    constant integer aID          = 'A000';
    // This is the Dummy ID
    constant integer dID          = 'h000';
    // How often should the Effect come (every LoopTime +1)
    constant integer speTime      = 12;
    
    // NONCHANGEABLE
    integer aLevel;
    location aPos;
    constant integer Angle        = 1;
    constant integer Level        = 2;
    constant integer Range        = 3;
    constant integer Unit         = 4;
    constant integer Time         = 5;
    constant integer Effect         = 6;
    group NoStack        = CreateGroup();
    unit Arrow;
    hashtable AKHash     = InitHashtable();
    
    function IsAbility() -> boolean{if ( GetSpellAbilityId() == aID ){return true;}else{return false;}}
    
    function GroupCon() -> boolean {
    if ( IsUnitAliveBJ(GetFilterUnit()) == true && IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(Arrow)) == true && IsUnitInGroup(GetFilterUnit(), NoStack) == false && IsUnitAliveBJ(Arrow) == true ){
        return true;} else { return false; }
    }
    
    function KnockbackLoop(){
        handle q = GetExpiredTimer(); 
        integer h = GetHandleId(q);
        real angle = LoadReal( AKHash, h, Angle);
        real range = LoadReal( AKHash, h, Range);
        unit u = LoadUnitHandle( AKHash, h, Unit);
        integer i = LoadInteger( AKHash, h, Effect);
        real speed = kSpeed*LoopTime;
        real x = GetUnitX(u) + (speed) * Cos(angle * bj_DEGTORAD);
        real y = GetUnitY(u) + (speed) * Sin(angle * bj_DEGTORAD);
        if ( IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) == true) {
            angle = 0 - angle;
            x = GetUnitX(u) + (speed) * Cos(angle * bj_DEGTORAD);
            y = GetUnitY(u) + (speed) * Sin(angle * bj_DEGTORAD);
            if ( IsTerrainPathable( x, y, PATHING_TYPE_WALKABILITY) == true) {
                angle = angle -180;
                x = GetUnitX(u) + (speed) * Cos(angle * bj_DEGTORAD);
                y = GetUnitY(u) + (speed) * Sin(angle * bj_DEGTORAD);
            }
        }
        if ( i >= speTime ){ DestroyEffect(AddSpecialEffect( speEffect, x, y)); i = 0;}
        SetUnitPosition( u, x, y);
        range = range - speed;
        SaveReal( AKHash, h, Range, range);
        SaveReal( AKHash, h, Angle, angle);
        SaveInteger( AKHash, h, Effect, i + 1);
        if (range <= 0){
            DestroyTimer(GetExpiredTimer());
            SetUnitPathing( u, true );
            GroupRemoveUnit( NoStack, u);
        } else if ( GetUnitState( u, UNIT_STATE_LIFE) <= 0 ) { DestroyTimer(GetExpiredTimer()); GroupRemoveUnit( NoStack, u); SetUnitPathing( u, true );}
    
    
    }
    
    function Knockback(){
        unit u = GetEnumUnit();
        real x = GetUnitX(u);
        real y = GetUnitY(u);
        timer t = CreateTimer();
        handle q = t;
        integer h = GetHandleId(q);
        real angle = AngleBetweenPoints( aPos, Location(x, y));
        DestroyEffect(AddSpecialEffect( speEffect, x, y));
        SaveReal( AKHash, h, Range, aLevel * kRange);
        SaveReal( AKHash, h, Angle, angle);
        SaveUnitHandle( AKHash, h, Unit, u);
        UnitDamageTarget( Arrow, u, kDamage * aLevel, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS);
        GroupAddUnit( NoStack, u);
        SetUnitPathing( u, false );
        TimerStart(t, LoopTime, true, function KnockbackLoop);
    }
    
    function Loop(){
        handle q = GetExpiredTimer(); 
        integer h = GetHandleId(q);
        real angle = LoadReal( AKHash, h, Angle);
        integer level = LoadInteger( AKHash, h, Level);
        real range = LoadReal( AKHash, h, Range);
        unit u = LoadUnitHandle( AKHash, h, Unit);
        real speed = aSpeed*LoopTime;
        real x = GetUnitX(u) + (speed) * Cos(angle * bj_DEGTORAD);
        real y = GetUnitY(u) + (speed) * Sin(angle * bj_DEGTORAD);
        group g = CreateGroup();
        if ( IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) == true) {
            angle = 0 - angle;
            x = GetUnitX(u) + (speed) * Cos(angle * bj_DEGTORAD);
            y = GetUnitY(u) + (speed) * Sin(angle * bj_DEGTORAD);
            if ( IsTerrainPathable( x, y, PATHING_TYPE_WALKABILITY) == true) {
                angle = angle -180;
                x = GetUnitX(u) + (speed) * Cos(angle * bj_DEGTORAD);
                y = GetUnitY(u) + (speed) * Sin(angle * bj_DEGTORAD);
            }
        }
        Arrow = u;
        aLevel = level;
        aPos = Location(GetUnitX(u), GetUnitY(u));
        GroupEnumUnitsInRange( g, x, y, PickUpAoE, Condition(function GroupCon));
        ForGroup( g, function Knockback);
        SetUnitFacing( u, angle);
        SetUnitPosition( u, x, y);
        range = range - speed;
        SaveReal( AKHash, h, Range, range);
        SaveReal( AKHash, h, Angle, angle);
        if (range <= 0){
            DestroyTimer(GetExpiredTimer());
            RemoveUnit( u);        
        }
        DestroyGroup(g);
        
    }
    
    function Cast(){
        unit u = GetTriggerUnit();
        real x = GetUnitX(u);
        real y = GetUnitY(u);
        timer t = CreateTimer(); 
        handle q = t;
        location p = GetSpellTargetLoc();
        real angle = AngleBetweenPoints(Location(x, y), p);
        unit dummy = CreateUnit ( Player(GetPlayerId(GetOwningPlayer(u))), dID, x, y, angle);
        integer h = GetHandleId(q);
        integer level = GetUnitAbilityLevel( u, aID);
        SaveReal( AKHash, h, Angle, angle);
        SaveInteger( AKHash, h, Level, level);
        SaveReal( AKHash, h, Range, level * aRange);
        SaveUnitHandle( AKHash, h, Unit, dummy);
        RemoveLocation(p);
        TimerStart(t, LoopTime, true, function Loop);
    }
    
    
    
    function onInit(){
        trigger t = CreateTrigger(  );
        TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT );
        TriggerAddCondition( t, Condition( function IsAbility ) );
        TriggerAddAction( t, function Cast );
    
    
    }



}
//! endzinc

Update
v3
Now uses Hashtables and improved abit the Triggering, if i have some Time, i will totally optimize this Spells.
v4
made it abit more customizable, removed the flying part
v5
Now Completly uses Zinc (Probably need some help to optimize it)

Keywords:
cbs,arrow, knockback, Zinc, Spell
Contents

ArrowKnockback v4 (Map)

Reviews
12th Dec 2015 IcemanBo: Too long as NeedsFix. Rejected. March 28, 2013 Magtheridon96: You're constantly running the timer and letting it expire throughout the game even while the trigger that runs whenever that timer expires is off. You...

Moderator

M

Moderator

12th Dec 2015
IcemanBo: Too long as NeedsFix. Rejected.

March 28, 2013
Magtheridon96:

  • You're constantly running the timer and letting it expire throughout the game even while the trigger that runs whenever that timer expires is off. You should start the timer as a repeating timer with a certain interval (0.03 is recommended) when an instance is created, and you should pause the timer when there are no instances left.
  • In CastArrow:
    • Store the triggering unit into a variable and use that variable instead of calling Triggering unit more than once.
    • Cache the level of the ability into a temporary variable.
    • Use triggering player instead of the owner of the triggering unit.
    • The ability should be stored into a variable for configuration in the Init trigger. You would then use that variable everywhere in your code instead of using the object directly. You would also avoid (Ability being cast) and just use the variable.
  • This system still kills bridges and other important destructables. I'd only recommend destroying trees. You can pull this off by having a hidden dummy peasant unit created on map initialization and given the locust ability. This unit will be ordered to "harvest" the picked destructable. Directly after that, if his current order is "harvest", then that destructable is indeed a tree and you can proceed with killing it.
  • The special effects created such as "Objects\Spawnmodels\Other\NeutralBuildingExplosion\NeutralBuildingExplosion.mdl" should be configurable from the Init trigger.
  • NEVER, and I mean NEVER, under any circumstances, should anyone be changing custom value, unless the resource is a Unit Indexer. Use a hashtable for data storage instead. I repeat, NEVER.
  • You shouldn't have debug messages in there like that. (Especially when they're not very useful)
  • KnockbackDamage should not be an array.
  • Instead of repeating (Key handle), you can use an integer (TempInteger) that you would set via a custom scripts (set udg_TempInteger = GetHandleId(udg_handle))
  • The ability you're adding and removing to units should be configurable from the Init trigger just like the main ability.
  • You're leaking the group variable "Group". Destroy it in the Move Arrow trigger after you're done with picking the units inside it. (You would do this: call DestroyGroup(udg_Group))
 
Level 5
Joined
Feb 5, 2008
Messages
109
Hm, you could make a ability requiring a target and when that ability is being cast save the target to a variable, then order the unit to cast the same ability again. Then when you choose a target, it checks whether the first target variable is set and if thats the case your effect triggers.
 
Level 5
Joined
Dec 8, 2008
Messages
102
good effects but a bit buggy, when u use the arrow fast enough in a short time one arrow will stack or a peasant will not land...its good gui triggering but u could add 1 thing: for each integer 1-100 is a complete wrong way for mui, make for each integer "variable1"-"variable2" do actions cause u can set variable1 whenever an arrow dies and variable2 whenever arrow is casted, so u dont waste 99 integers
 
Top