• 🏆 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!

[JASS] {idea} [vJass] (System) CustomAttack

Status
Not open for further replies.
Thanks for the feedback guys, the next thing I am doing is "virtualizing" the attack so you actually attack without attacking at all, this will be amazing because it gives me the possibility to recreate the wc3 attack in every way.

The next spell I am doing is a spell that changes the attacks of units in an AOE.
Everytime an enemy unit is attacking around (with the buff) it'll heal damage instead of dealing it.
 
#Update.

You can now emulate orders (with animations) and this engine (CustomOrder) will be connected with CustomAttack.

This allows you to be able to cast a spell at the same time you're attacking an unit. (And also I can now fully trigger the attack stuff, from the attack target detection, with the animation playing and missile throwing until the damaging and attack finishing).

The first beta will include an missile slow aura and a passive ability that sometimes triggers 2 attacks.
 
Level 22
Joined
Dec 31, 2006
Messages
2,216
Could you add a the possibility of letting other units "charge" another unit's attack like the Prism Towers in Red Alert 2 or Spectrum Tower in Red Alert 3?

Here's an image:
611354-spectrum_tower_large.jpg


In both RA2 and RA3 several towers can assist.

O o
/¯¯_¯¯_¯_¯__¯__¯¯__¯¯__¯__¯¯_¯__¯__¯_¯_ ¯ _¯ SHOOP DA WHOOP!
\___¯¯_¯_¯¯__¯¯¯¯_¯¯¯__¯¯¯¯_¯¯¯¯_¯¯¯¯_¯ ¯_ ¯
 
nice :D
now test it with new fps rate ^^

offtopic:
O o
/¯¯_¯¯_¯_¯__¯__¯¯__¯¯__¯__¯¯_¯__¯__¯_¯_ ¯ _¯ SHOOP DA WHOOP!
\___¯¯_¯_¯¯__¯¯¯¯_¯¯¯__¯¯¯¯_¯¯¯¯_¯¯¯¯_¯ ¯_ ¯

O o
/¯_____________________
| SHOOP DA WHOOP!!!
\_¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

is the right one ;)
 
Level 4
Joined
Feb 18, 2008
Messages
69
Well, this system seems ultrainteresting, I will totally use it in my map, since current projectile system sucks.

Although, I will have to redo some spells and my whole attack system since my one works differently, I block damage when any unit is damaged and then do my stuff (ranged units have no model for missiles and missile speed 99999).. my system looks like this..

It's not anything for public so it looks messy and maybe confusing:

JASS:
library Attack initializer OnInit requires Damage, Projectile, Event, AttackData, UnitData
    
    globals
        private trigger    OnDAMAGE = CreateTrigger()
        private Event      OnAttackEvent = 0
        private Event      OnProjEndEvent = 0
        private AttackData AttackDataX   = 0
        private unit       SourceUnit    = null
        private unit       TargetUnit    = null
        private Projectile ProjectileData = 0
        private damagetype DamageType    = DAMAGE_TYPE_NORMAL
        private attacktype AttackType    = ATTACK_TYPE_NORMAL
        private real       DamageDealt   = 0.00
    endglobals
    
    public function RegisterEvent takes trigger whichTrig returns EventReg
        return OnAttackEvent.register(whichTrig)
    endfunction
    
    public function RegisterProjEndEvent takes trigger whichTrig returns EventReg
        return OnProjEndEvent.register(whichTrig)
    endfunction
    
    public function GetSource takes nothing returns unit
        return SourceUnit
    endfunction
    
    public function GetTarget takes nothing returns unit
        return TargetUnit
    endfunction
    
    public function GetDamage takes nothing returns real
        return DamageDealt
    endfunction
    
    public function GetDamageType takes nothing returns damagetype
        return DamageType
    endfunction
    
    public function GetAttackType takes nothing returns attacktype
        return AttackType
    endfunction
    
    public function GetAttackData takes nothing returns AttackData
        return AttackDataX
    endfunction
    
    public function GetProjectileData takes nothing returns Projectile
        return ProjectileData
    endfunction
    
    function AttackDamageTarget takes unit theKiller, unit theVictim, real theDamage, boolean isRanged, attacktype attackType, damagetype damageType returns nothing
        local UnitData ud = UnitData[theVictim]
        local real origDmg = theDamage
        local real dmg = theDamage
        if not IsUnitType(theVictim, UNIT_TYPE_DEAD) then
            set DamageType = damageType
            if GetRandomReal(0., 100.) >= ud.evadeChnc then
                if GetRandomReal(0., 100.) < ud.blockChnc then
                    if dmg - ud.blockMlt >= 5. then
                        set dmg = dmg - ud.blockMlt
                    else
                        set dmg = 5.
                    endif
                    call BlockTextTag(theVictim, origDmg - dmg)
                endif
                call TriggeredAttack(theKiller, theVictim, dmg, isRanged, attackType, damageType)
                if (ud.returndamage > .0 or ud.returndamageItem > .0) and IsUnitType(theKiller, UNIT_TYPE_STRUCTURE) == false then
                    call Damage_Physical(theVictim, theKiller, dmg * (ud.returndamage * .01 + ud.returndamageItem * .01), attackType, false, false)
                endif
            else
                call DodgeTextTag(theVictim)
            endif
        endif
    endfunction
    
    public function OnUnitImpact takes Projectile p, unit whichUnit returns nothing
        local AttackData attad  = AttackData_GetUnit(GetUnitTypeId(whichUnit))
        if whichUnit == p.target then
            if p.unitDamage > 0. then
                set DamageType = p.damageType
                set AttackType = p.attackType
                set SourceUnit  = p.caster
                set TargetUnit  = p.target
                set DamageDealt = p.unitDamage
                set AttackDataX = attad
                set ProjectileData = p
                call OnProjEndEvent.fire()
                call AttackDamageTarget(p.caster, whichUnit, p.unitDamage, true, p.attackType, p.damageType)
            endif
            call p.terminate()
        endif
    endfunction
    
    public function OnProjectileLoop takes Projectile p returns nothing
        if IsUnitType(p.target, UNIT_TYPE_DEAD) and p.enableHoming then
            set p.totalDist = DBPXY(GetUnitX(p.caster), GetUnitY(p.caster), GetUnitX(p.target), GetUnitY(p.target))
            set p.toDist = true
            set p.enableHoming = false
        endif
    endfunction
    
    public function GetAttackDamage takes unit u returns real
        if u != null then
            if IsUnitType(u, UNIT_TYPE_HERO) then
                return (UnitData[u].baseDmg + Status[u].getDamageBonus() + GetHeroMainAttValue(u, UnitData[u].mainAtt, true)) + UnitData[u].numberDmg * GetRandomInt(1, UnitData[u].sidesDmg)
            else
                return (UnitData[u].baseDmg + UnitData[u].upgradeDmg + Status[u].getDamageBonus()) + UnitData[u].numberDmg * GetRandomInt(1, UnitData[u].sidesDmg)
            endif
        endif
        return 0.0
    endfunction 

    private function onDamage takes nothing returns boolean
        local unit att = GetEventDamageSource()
        local unit trg = GetTriggerUnit()
        local real ax  = GetUnitX(att)
        local real ay  = GetUnitY(att)
        local real tx  = GetUnitX(trg)
        local real ty  = GetUnitY(trg)
        local real ang = Atan2((ty - ay),(tx - ax))
        local real dmg = 0.
        local AttackData attad  = AttackData_GetUnit(GetUnitTypeId(att))
        local AttackData trgad  = AttackData_GetUnit(GetUnitTypeId(trg))
        local UnitData attud = UnitData[att]
        local UnitData trgud = UnitData[trg]
        local Projectile p   = 0
        if AttackData_IsRegistered(GetUnitTypeId(att)) and AttackData_IsRegistered(GetUnitTypeId(trg)) and GetEventDamage() > 0 then
            if Damage_IsAttack() then
                // Blocking all damage done
                call Damage_BlockAll()
                if GetRandomReal(0., 100.) >= attud.missChnc then
                    set dmg = GetAttackDamage(att)
                    if GetRandomReal(0., 100.) < attud.critChnc then
                        set dmg = dmg * (1. + attud.critMlt)
                        call CriticalTextTag(att, dmg)
                    endif
                    if GetRandomReal(0., 100.) < attud.stunChance then
                        call BuffStun.create(trg).destroyTimed(attud.stunTime)
                    endif
                    if (attud.lifesteal > 0 or attud.lifestealItem > 0) and IsUnitType(trg, UNIT_TYPE_STRUCTURE) == false then
                        call SetWidgetLife(att, GetWidgetLife(att) + dmg * (attud.lifesteal * .01 + attud.lifestealItem * .01))
                        call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Undead\\VampiricAura\\VampiricAuraTarget.mdl", att, "origin"))
                    endif
                else
                    call MissTextTag(att)
                endif
                
                if attud.isRanged then
                    set p = Projectile.create(ax, ay, attud.missileHgt, ang)
                    set p.caster        = att
                    set p.target        = trg
                    set p.owner         = GetOwningPlayer(att)
                    set p.effectPath    = attud.missileFx
                    set p.scaleSize     = attud.missileScl
                    set p.unitDamage    = dmg
                    set p.attackType    = attud.attackTyp
                    set p.damageType    = DAMAGE_TYPE_NORMAL
                    set p.unitCollision = attud.missileCol
                    set p.enableHoming  = true
                    set p.throughGround = true
                    set p.onUnit        = OnUnitImpact
                    set p.onLoop        = OnProjectileLoop
                    if attud.missileNrm then
                        call p.projectNormal(tx, ty, GetUnitFlyHeight(trg) + attud.missileHgt, attud.missileSpd)
                    else
                        call p.projectArcing(tx, ty, GetUnitFlyHeight(trg) + attud.missileHgt, attud.missileSpd, attud.missileArc)
                    endif
                else
                    call AttackDamageTarget(att, trg, dmg, false, attud.attackTyp, DAMAGE_TYPE_NORMAL)
                endif
                set DamageType = DAMAGE_TYPE_NORMAL
                set AttackType = attud.attackTyp
                set SourceUnit  = att
                set TargetUnit  = trg
                set DamageDealt = dmg
                set AttackDataX = attad
                set ProjectileData = p
                call OnAttackEvent.fire()
            elseif Damage_IsSpell() then
                set dmg = GetEventDamage()
                if attud.spellDamage != 0 then 
                    set dmg = dmg * (1.0 + (attud.spellDamage * .01))
                endif
                if trgud.blockMagic != 0. then
                    set dmg = dmg * (1.0 - (trgud.blockMagic * .01))
                endif
                if dmg > GetEventDamage() then
                    call DisableTrigger(OnDAMAGE)
                    call Damage_Spell(att, trg, dmg)
                    call EnableTrigger(OnDAMAGE)
                else
                    call Damage_Block(GetEventDamage() - dmg)
                endif
            endif
        endif
        set att = null
        set trg = null
        return false
    endfunction

    private function OnInit takes nothing returns nothing
        set OnAttackEvent = Event.create()
        set OnProjEndEvent = Event.create()
        call Damage_RegisterEvent(OnDAMAGE)
        call TriggerAddCondition(OnDAMAGE, Condition(function onDamage))
    endfunction

endlibrary

Anyway, good luck on this! Finish it no matter what.
 
Level 4
Joined
Feb 18, 2008
Messages
69
Yep, but I didn't want to make system work like you are doing, but now when I see it here, it seems it will work very good, so now I'm up to it.

Well, this will trigger an attack after animation: (And play the animation automatically)
JASS:
AttackOrder.create(attacker, target)
Can it be any easier? (Every data is loaded automatically)

Well, this one also triggers melee attacks instantly..
You said you have an missile speed of 9999, and that is not instantly, but has a delay of the interval you check the missiles.

I guess so.
It really will :)
 
Level 4
Joined
Feb 18, 2008
Messages
69
You said you have an missile speed of 9999, and that is not instantly, but has a delay of the interval you check the missiles.

Although, I will have to redo some spells and my whole attack system since my one works differently, I block damage when any unit is damaged and then do my stuff (ranged units have no model for missiles and missile speed 99999)

I said ranged units have missile speed 99999 so I they damage unit (relatively) instantly and then I run my missile which has saved data about damage.

For melee units it damages instantly.

But whatever this is not important, why are we discussing it. :xxd:

What else you need to add to the system before beta? :smile:
 
Level 22
Joined
Dec 31, 2006
Messages
2,216
Yes, you can already do that.

I will make an example spell to make target units attack temporary ranged if you wish. :D

Nice :D

Does it support that one tower "charges" another tower which again "charges" a third tower which attacks? It might be a bit complicated, but it would be awesome :D

Example:
(t == tower, - == laz00r, x == target)
t-t-t-x
 
Level 8
Joined
Aug 6, 2008
Messages
451
You can set your ranged attack to be instant in object editor, so it has no missile, nor missile speed.



edit. Nevermind, you were talking about different thing. Just ignore me here pls.
 
Level 6
Joined
Oct 10, 2009
Messages
1,425
so, when can we hope for a beta ?
i could have sworn you said it was like a week ago or something :p
sorry, but i really cannot wait for this system :D,
but can you edit the description with all of it's capabilities? that would be helpful so people won't post suggestions that you have already implemented :p
 
Level 25
Joined
Mar 25, 2004
Messages
4,880
Not sure if anyone else suggested this, but it's just an idea that popped into mind. What about having the unit taking damage or a crit damage play their death animation for a quick moment, but not fully playing the animation, so that it looks like they're getting hurt?
 
Level 11
Joined
Apr 29, 2007
Messages
826
Not sure if anyone else suggested this, but it's just an idea that popped into mind. What about having the unit taking damage or a crit damage play their death animation for a quick moment, but not fully playing the animation, so that it looks like they're getting hurt?

Doing that would play the dying unit sound everytime a unit is hit.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Doing that would play the dying unit sound everytime a unit is hit.

Well I imagine it would only be on critical or "special" damage. This isn't actually a bad idea at all, but you'd have to play a very very small blip of the death animation so that you don't include any effects that may be included (like 0.1 seconds of the animation). This would also allow for the possibility of "attack recovery" like in Diablo II :D
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
I'm not talking about evasion, I'm talking about a time that is required after being successfully attacked before you can do anything. If someone slashes at you with a sword while you're in the middle of swinging it's not like you're just going to take a little damage and shrug it off. It's going to hurt.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
I wouldn't recommend using Pause because then the unit cannot issue any orders (or queue them) while it is recovering. I suggest using something like Rising_Dusk's "UnitStatus" library.

Also what I was talking about isn't evasion, but that you can "undo" attacks later. Like a spell that does:
"Removes the last 4 attacks from the unit and will cause the unit to do the same attacks to its attacker".

Eh, that's not particularly difficult. You don't need to use a hashtable either, in fact it would probably be better/easier if you used AutoIndex to index the units. This would make referencing the last X instances of damage a breeze.
 
Eh, that's not particularly difficult. You don't need to use a hashtable either, in fact it would probably be better/easier if you used AutoIndex to index the units. This would make referencing the last X instances of damage a breeze.
And where should I store all the values then? o_O
I mean I need to store X last attacks for Y units.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
JASS:
struct UnitDamage

    real array prevDamage [DAMAGE_INSTANCES]
    integer prevDamageN = 0

    method addDamage takes real damageAmount returns nothing
        local integer i = 0
        if prevDamageN < DAMAGE_INSTANCES then
            set prevDamage[prevDamageN] = damageAmount
            set prevDamageN = prevDamageN + 1
        else
            loop
                exitwhen(i == DAMAGE_INSTANCES-1)
                set prevDamage[i] = prevDamage[i+1]
                set i = i + 1
            endloop
        endif
    endmethod

    implement AutoCreate
    implement AutoDestroy
endstruct
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
It uses AutoIndex, which uses SetUnitUserData I believe. I think there is a configuration boolean that switches to using a hashtable if using the user-data isn't acceptable, but if you're using an indexing system then there's no point in not using it since custom data can just be saved per struct.
 
Level 6
Joined
Nov 3, 2008
Messages
117
Ok correct me if i'm mistaken, but the code snipet berb posted there will malfunction when you will have lots of units on the map. I dont know the value of DAMAGE_INSTANCES but array members reduce the structs instance limit, drastically. So lets say DAMAGE_INSTANCES is 5, so you have a max of 8190/5=1638 instances. Now you use this for a unit with an index higher than 1638 and nothing happens.
If you want this to work for all units, the only possibility means "hashtable" o_O
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Obviously. It goes without saying that having 2000 units is not a good idea. If you've got 1638 units that you aren't recycling constantly then your map is going to leak balls anyways. That isn't a "malfunction", it's a limitation of JASS since we are only allowed 8191 array instances. If you tried to store 2000 damage instances previous it would probably "malfunction" too very quickly, the idea is that you're not stressing Warcraft with ridiculous values.
 
Level 6
Joined
Nov 3, 2008
Messages
117
well Auto Index is recycling the Units. And as i said this will just malfunction if you have more then e.g. 1638 instances. Since this is a system that can be used everywhere, there will be people who reach this limit in there map. with a hashtable you have unlimited instances. But this is up to Anachron if he makes it faster or more safe.
 
Status
Not open for further replies.
Top