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

[vJASS] Complex attacking system requiring a lot of data sorting

Status
Not open for further replies.
Level 19
Joined
Oct 12, 2007
Messages
1,821
Hello there.

I'm trying to work on an attacking system for my map and I could use some help regarding letting a couple of events happen in a certain order.

I'll explain to you how the attacking works in the map:

Attacking goes in groups. So there's a group with 'attackers' and a group with 'defenders'. The maximum amount of units in a group is 16.
Every unit can do a couple of attacks equal to his strength. So a strength of 3 allows the unit to do 3 attacks. A full group of those units will attack in 3x 16 hits because a full group is 16 units. Clear? ;-)
But inbetween those 3*16 hits the defenders can also strike. The order is basically like this in a 16vs16 fight:

Attackers Strike (x16)-> Defenders Strike (x16 (if all 16 survived))-> Attackers Strike (x16 (if all survived...)) - Defenders Strike... etc

This will go on until both sides run out of attacks.
If, let's say, the attackers have 3 attacks and the defenders have only 1 it will work like this:
Attackers strike -> Defenders strike -> Attackers strike -> Attackers strike


Now there are also some exceptions:
There's an ability called 'First Strike' and an ability called 'Last Strike'. This can seriously make the order a bit more difficult.

If in a scenario the attackers have Last Strike and the Defenders are normal, and both sides have 2 attacks, it will go like this:

Defenders strike > Attackers Strike > Defender sstrike > Attackers strike...

The above outcome will be the same when the defenders have first strike and the attackers are normal.


Now the tricky part comes that a group of units can have some units in it with First Strike and some others without it. This could lead to something like this:

Defenders (with First Strike) strike -> Attackers strike -> Defenders (with First Strike + normal defenders) strike -> Attackers strike -> Defenders (without First Strike) strike.

The above result means that all units must've struck twice, but the 'First Strike Defenders' just started attacking a bit earlier.



Enough of that! Here's my question:

Would someone know a good way to create this kind of an order and execute it in a correct but efficient way? (Don't mind the BJ's and all I use at the bottom of the code. This is just to practise)

Here's the part of the code I have so far that creates the order.
There are some flaws with it.

JASS:
call GroupEnumUnitsInRect(ATTACKERS, Field[ax][ay], null)
        call GroupEnumUnitsInRect(DEFENDERS, Field[dx][dy], null)
        
        ///Adding First Strike Attackers first
        call GroupEnumUnitsInRect(g, Field[ax][ay], null)
        set t = FirstOfGroup(g)
        loop
        exitwhen t == null
            if GetUnitAbilityLevel(t, FIRST_STRIKE) > 0 and GetUnitAbilityLevel(t, LAST_STRIKE) == 0 then
                set AttackOrder[i] = t
                set i = i + 1
                call PauseUnit(t, true)
            endif
            call GroupRemoveUnit(g, t)
            set t = FirstOfGroup(g)
        endloop
        call GroupClear(g)
        
        ///Adding First Strike Defenders second
        call GroupEnumUnitsInRect(g, Field[dx][dy], null)
        set t = FirstOfGroup(g)
        loop
        exitwhen t == null
            if GetUnitAbilityLevel(t, FIRST_STRIKE) > 0 and GetUnitAbilityLevel(t, LAST_STRIKE) == 0 then
                set AttackOrder[i] = t
                set i = i + 1
                call PauseUnit(t, true)
            endif
            call GroupRemoveUnit(g, t)
            set t = FirstOfGroup(g)
        endloop
        call GroupClear(g)
        
        ///Adding Normal Attackers third
        call GroupEnumUnitsInRect(g, Field[ax][ay], null)
        set t = FirstOfGroup(g)
        loop
        exitwhen t == null
            if GetUnitAbilityLevel(t, FIRST_STRIKE) == 0 and GetUnitAbilityLevel(t, LAST_STRIKE) == 0 then
                set AttackOrder[i] = t
                set i = i + 1
                call PauseUnit(t, true)
            elseif GetUnitAbilityLevel(t, FIRST_STRIKE) > 0 and GetUnitAbilityLevel(t, LAST_STRIKE) > 0 then
                set AttackOrder[i] = t
                set i = i + 1
                call PauseUnit(t, true)
            endif
            call GroupRemoveUnit(g, t)
            set t = FirstOfGroup(g)
        endloop
        call GroupClear(g)
        
        ///Adding Normal Defenders fourth
        call GroupEnumUnitsInRect(g, Field[dx][dy], null)
        set t = FirstOfGroup(g)
        loop
        exitwhen t == null
            if GetUnitAbilityLevel(t, FIRST_STRIKE) == 0 and GetUnitAbilityLevel(t, LAST_STRIKE) == 0 then
                set AttackOrder[i] = t
                set i = i + 1
                call PauseUnit(t, true)
            elseif GetUnitAbilityLevel(t, FIRST_STRIKE) > 0 and GetUnitAbilityLevel(t, LAST_STRIKE) > 0 then
                set AttackOrder[i] = t
                set i = i + 1
                call PauseUnit(t, true)
            endif
            call GroupRemoveUnit(g, t)
            set t = FirstOfGroup(g)
        endloop
        call GroupClear(g)
        
        ///Adding Slow Attackers fifth
        call GroupEnumUnitsInRect(g, Field[ax][ay], null)
        set t = FirstOfGroup(g)
        loop
        exitwhen t == null
            if GetUnitAbilityLevel(t, FIRST_STRIKE) == 0 and GetUnitAbilityLevel(t, LAST_STRIKE) > 0 then
                set AttackOrder[i] = t
                set i = i + 1
                call PauseUnit(t, true)
            endif
            call GroupRemoveUnit(g, t)
            set t = FirstOfGroup(g)
        endloop
        call GroupClear(g)
        
        ///Adding Slow Defenders fourth
        call GroupEnumUnitsInRect(g, Field[dx][dy], null)
        set t = FirstOfGroup(g)
        loop
        exitwhen t == null
            if GetUnitAbilityLevel(t, FIRST_STRIKE) == 0 and GetUnitAbilityLevel(t, LAST_STRIKE) > 0 then
                set AttackOrder[i] = t
                set i = i + 1
                call PauseUnit(t, true)
            endif
            call GroupRemoveUnit(g, t)
            set t = FirstOfGroup(g)
        endloop
        call GroupClear(g)
        
        loop
        exitwhen f > i
        if f <= i then
            if IsUnitAliveBJ(AttackOrder[f]) == true then  ///change later
                set Attacks = GetHeroStr(AttackOrder[f], true)
                loop
                exitwhen Attacks <= 0
                    set id = GetUnitId(AttackOrder[f])
                    call SetUnitAnimation(AttackOrder[f], "attack")
                    call QueueUnitAnimation(AttackOrder[f], "stand") 
                    if IsUnitInGroup(AttackOrder[f], ATTACKERS) == true then
                        set Target = GroupPickRandomUnit(DEFENDERS) ///change later
                    elseif IsUnitInGroup(AttackOrder[f], DEFENDERS) == true then
                        set Target = GroupPickRandomUnit(ATTACKERS) ///change later
                    endif
                    set Armor = Unit_Armor[GetUnitId(Target)]
                    set Damage = GetRandomInt(Unit_DamageMin[id], Unit_DamageMax[id])
                    set Attacks = Attacks - 1
                endloop
                set f = f + 1
            else
                set f = f + 1
            endif
        endif
        endloop
        set i = 0
        set f = 0
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
A defender responds to being attacked. Queue up attackers normally, then apply all attacks. For each attack, attack defender responds, either by an immediate attack before damage is calculated, or by an attack immediately after damage, or by attack during damage. Compare the priority of attacker to defender for things like first strike. This will run everything in the correct order.

If you want to worry about speed, don't sort, put the stuff on timers representing cooldown.
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,198
Defenders (with First Strike) strike -> Attackers strike -> Defenders (with First Strike + normal defenders) strike -> Attackers strike -> Defenders (without First Strike) strike.
You are making the mistake thinking that this is the correct order. The actual correct order should be...
Defenders FS -> Attackers -> Defenders -> Defenders FS -> Attackers -> Defenders

You simply give 2 defender rolls in a row for the same results as if they happened at the same time. This means there is actually a pre-determined execution order for combat.
Code:
loop until all units done...
  Attackers FS
  Defenders FS
  Attackers
  Defenders
  Attackers LS
  Defenders LS
endloop

If 2 different defenders go in a row, you merge the graphics so it appears they attack at the same time. You wait for results if you encounter the opposite side getting a round. You also wait for results if the loop goes full circle (so the same units do not attack twice at once)

Each can be a separate list you iterate through. No need to order it or anything, simply add the units to the appropriate groups.

To deal with strength give each unit an "attack" counter which replenishes when combat starts. When it is 0 then they miss the round of fighting completely when it comes. When every unit in the fight have 0 attacks left then exit the loop and the combat ends.
 
Level 19
Joined
Oct 12, 2007
Messages
1,821
I see.
So you advice to use 6 groups instead of 2, and divide the units among them based on having or not having the FS or LS abilities?
 
Level 12
Joined
Oct 16, 2010
Messages
680
So you advice to use 6 groups instead of 2, and divide the units among them based on having or not having the FS or LS abilities?

no 6 groups needed, he just points out the repeating attack order

more like start with attack group and only attack with units has FS
then attack with defender with FS
then attacker with none
defender with none
attacker with LS
defender with LS

u don't need 6 groups for that just some conditions while looping through the units in the 2 groups
 
Level 19
Joined
Oct 12, 2007
Messages
1,821
no 6 groups needed, he just points out the repeating attack order

more like start with attack group and only attack with units has FS
then attack with defender with FS
then attacker with none
defender with none
attacker with LS
defender with LS

u don't need 6 groups for that just some conditions while looping through the units in the 2 groups

Yep you're right. Thanks.
 
Level 19
Joined
Oct 12, 2007
Messages
1,821
I've progressed quite much since the beginning of this post. However I stumbled on some problems again. The 'rules' are pretty intense so it might take a while to get what I'm doing. I'll try to explain that first.

As described in posts above, the attack order is as follows:
- Attackers with First Strike
- Defenders with First Strike
- Attackers
- Defenders
- Attackers with Last Strike
- Defenders with Last Strike

If a unit has First Strike + Last Strike, it will count as if he had none.

Now there are also other abilities that make it more complex. Here are the rules regarding Charge, Block, Parry, Hold Your Ground and Trample.

Charge: Gives this unit a +1 on its first damage outcome whenever it performs an Opening Strike when attacking and ignores Last Strike effects if the charging unit has it.

Block: Gives this unit a +1 on its defense against an enemy's Opening Strike or against any ranged attack. (ranged attacks not implemented yet)

Parry: Gives this unit a +1 on its defense whenever the enemy strikes back from an attack.

Hold Your Ground: Gives this unit +1 defense, First Strike, and +1 on its damage outcome when being attacked by mounted units.

Trample: Ignores the effects of Hold your Ground when attacking an enemy that has it.

So especially Hold Your Ground and Trample make things even more difficult. So far I think I've managed quite well, however there are a lot of things inside the loop now.

In order to test I placed 3 different units on the field:
- Spearmen (with Hold Your Ground)
- Knights (with Trample, Charge and Block) - These are mounted
- Riders (with Block) - These are mounted
None of the above have First Strike or Last Strike. The only 'First Strike' that will come up is when Riders attack Spearmen, because this will activate their Hold Your Ground. And since Riders don't have Trample, they will have a disadvantage.

What happened while testing was this:
- Knights vs Spearmen - Damaging event went off, Spearmen died without having used their Hold Your Ground. (success!)
- Riders vs Spearmen - Damaging event didn't go off, nobody even took damage, but with teststrings I saw that Hold Your Ground did grant spearmen First Strike, and it also make them take 1 less damage and deal 1 more damage (in calculation). The only problem was that the damaging event at the end of the code didn't start for some reason. I'm wondering if the info within the loop became too heavy or something.


JASS:
library CombatSnippets requires UnitIndexing
globals
    private group ATTACKERS = CreateGroup()
    private group DEFENDERS = CreateGroup()
    private group TempGroup = CreateGroup()
    private integer FIRST_STRIKE = 'A00A'
    private integer LAST_STRIKE = 'A00D'
    private integer CHARGE = 'A00G'
    private integer BLOCK = 'A00N'
    private integer PARRY = 'A00M'
    private integer HOLD_YOUR_GROUND = 'A01E'
    private integer TRAMPLE = 'A00H'
endglobals

function IsUnitClassified takes unit u, string class returns boolean
    local string n = GetUnitName(u)
    local integer l = StringLength(class)
    local integer ln = StringLength(n)
    local integer i = 0
    local boolean check = false
    
    if class == n then
        set check = true
    endif
 
    loop
    exitwhen i > ln or check == true
        if SubString(n, i, i+l) == class then
            set check = true
        endif
        set i = i + 1
    endloop

    return check
endfunction

function GroupRandomUnit takes group g, boolean temphp returns unit
    local unit u = null
    local integer t = 0
    local integer curmax = 0
    local unit chosen = null
    
    loop
        set u = FirstOfGroup(g)
        exitwhen u == null
        if (Unit_TempHP[GetUnitId(u)] > 0 and temphp == true) or temphp == false then
            set t = GetRandomInt(1,100)
            if t>curmax then
                set curmax=t
                set chosen=u
            endif
        endif
        call GroupRemoveUnit(g, u)
        call GroupAddUnit(TempGroup, u)
    endloop
    
    loop
        set u = FirstOfGroup(TempGroup)
        exitwhen u == null
        call GroupRemoveUnit(TempGroup, u)
        call GroupAddUnit(g, u)
    endloop
    
    return chosen
endfunction

function Combat_Regiment takes integer AX, integer AY, integer DX, integer DY returns nothing
    local integer CombatRounds = 0
    local integer HighestStr = 0
    local unit u
    local unit t
    local integer id = 0
    local integer id2 = 0
    local integer damage = 0
    local integer openingstrike = 0
    
    
    call GroupEnumUnitsInRect(ATTACKERS, Field[AX][AY], null)
    call GroupEnumUnitsInRect(DEFENDERS, Field[DX][DY], null)
    
    loop
    set u = FirstOfGroup(ATTACKERS)
    exitwhen u == null
        set id = GetUnitId(u)
        set Unit_TempHP[id] = R2I(GetUnitState(u, UNIT_STATE_LIFE))
        set Unit_Attacks[id] = GetHeroStr(u, true)
        if GetHeroStr(u, true) > HighestStr then
            set HighestStr = GetHeroStr(u, true)
        endif
        call GroupRemoveUnit(ATTACKERS, u)
    endloop
    loop
    set u = FirstOfGroup(DEFENDERS)
    exitwhen u == null
        set id = GetUnitId(u)
        set Unit_TempHP[id] = R2I(GetUnitState(u, UNIT_STATE_LIFE))
        set Unit_Attacks[id] = GetHeroStr(u, true)
        if GetHeroStr(u, true) > HighestStr then
            set HighestStr = GetHeroStr(u, true)
        endif
        call GroupRemoveUnit(DEFENDERS, u)
    endloop
    
    loop
    exitwhen CombatRounds > HighestStr
    
        set CombatRounds = CombatRounds + 1
        ///First Strike Attackers///
        loop
        set u = FirstOfGroup(ATTACKERS)
        set t = GroupRandomUnit(DEFENDERS, true)
        exitwhen u == null
            if (GetUnitAbilityLevel(u, FIRST_STRIKE) > 0 and GetUnitAbilityLevel(u, LAST_STRIKE) == 0 ) or (GetUnitAbilityLevel(u, FIRST_STRIKE) > 0 and GetUnitAbilityLevel(u, CHARGE) > 0 and GetUnitAbilityLevel(u, LAST_STRIKE) > 0 ) then
                set id = GetUnitId(u)
                set openingstrike = 1
                ///The attack calculation
                if Unit_Attacks[id] > 0 then
                    set id2 = GetUnitId(t)
                    set damage = GetRandomInt(Unit_DamageMin[id], Unit_DamageMax[id]) - Unit_Armor[id2]
                    if GetUnitAbilityLevel(u, CHARGE) > 0 and openingstrike == 1 then
                        set damage = damage + GetUnitAbilityLevel(u, CHARGE)
                    endif
                    if GetUnitAbilityLevel(u, TRAMPLE) == 0 and GetUnitAbilityLevel(t, HOLD_YOUR_GROUND) > 0 and IsUnitClassified(u, "Mounted") then
                        set damage = damage - GetUnitAbilityLevel(t, HOLD_YOUR_GROUND)
                    endif
                    if GetUnitAbilityLevel(t, BLOCK) > 0 and openingstrike == 1 then
                        set damage = damage - GetUnitAbilityLevel(t, BLOCK)
                    endif
                    if GetUnitAbilityLevel(t, PARRY) > 0 and openingstrike != 1 then
                        set damage = damage - GetUnitAbilityLevel(t, PARRY)
                    endif
                    set Unit_TempHP[id2] = Unit_TempHP[id2] - damage
                    set Unit_Attacks[id] = Unit_Attacks[id] - 1
                endif
            endif
            call GroupRemoveUnit(ATTACKERS, u)
        endloop
        call GroupEnumUnitsInRect(ATTACKERS, Field[AX][AY], null)
        
        ///First Strike Defenders///
        loop
        set u = FirstOfGroup(DEFENDERS)
        set t = GroupRandomUnit(ATTACKERS, true)
        exitwhen u == null
            if (GetUnitAbilityLevel(u, FIRST_STRIKE) > 0 and GetUnitAbilityLevel(u, LAST_STRIKE) == 0) or (GetUnitAbilityLevel(u, LAST_STRIKE) == 0 and GetUnitAbilityLevel(u, HOLD_YOUR_GROUND) > 0 and GetUnitAbilityLevel(t, TRAMPLE) == 0 and IsUnitClassified(t, "Mounted")) then
                set id = GetUnitId(u)
                if openingstrike == 0 then
                    set openingstrike = 2
                endif
                ///The attack calculation
                if Unit_Attacks[id] > 0 then
                    set id2 = GetUnitId(t)
                    set damage = GetRandomInt(Unit_DamageMin[id], Unit_DamageMax[id]) - Unit_Armor[id2]
                    if GetUnitAbilityLevel(t, BLOCK) > 0 and openingstrike == 2 then
                        set damage = damage - GetUnitAbilityLevel(t, BLOCK)
                    endif
                    if GetUnitAbilityLevel(t, TRAMPLE) == 0 and GetUnitAbilityLevel(u, HOLD_YOUR_GROUND) > 0 then
                        set damage = damage + 1
                    endif
                    if GetUnitAbilityLevel(t, PARRY) > 0 and openingstrike != 2 then
                        set damage = damage - GetUnitAbilityLevel(t, PARRY)
                    endif
                    set Unit_TempHP[id2] = Unit_TempHP[id2] - damage
                    set Unit_Attacks[id] = Unit_Attacks[id] - 1
                endif
            endif
            call GroupRemoveUnit(DEFENDERS, u)
        endloop
        call GroupEnumUnitsInRect(DEFENDERS, Field[DX][DY], null)
        
        ///Normal Attackers///
        loop
        set u = FirstOfGroup(ATTACKERS)
        set t = GroupRandomUnit(DEFENDERS, true)
        exitwhen u == null
            if (GetUnitAbilityLevel(u, FIRST_STRIKE) == 0 and GetUnitAbilityLevel(u, LAST_STRIKE) == 0 ) or ((GetUnitAbilityLevel(u, FIRST_STRIKE) > 0 or GetUnitAbilityLevel(u, CHARGE) > 0) and GetUnitAbilityLevel(u, LAST_STRIKE) > 0 ) then
                set id = GetUnitId(u)
                if openingstrike == 0 then
                    set openingstrike = 3
                endif
                ///The attack calculation
                if Unit_Attacks[id] > 0 then
                    set id2 = GetUnitId(t)
                    set damage = GetRandomInt(Unit_DamageMin[id], Unit_DamageMax[id]) - Unit_Armor[id2]
                    if GetUnitAbilityLevel(u, CHARGE) > 0 and openingstrike == 3 then
                        set damage = damage + GetUnitAbilityLevel(u, CHARGE)
                    endif
                    if GetUnitAbilityLevel(t, BLOCK) > 0 and openingstrike == 3 then
                        set damage = damage - GetUnitAbilityLevel(t, BLOCK)
                    endif
                    if GetUnitAbilityLevel(u, TRAMPLE) == 0 and GetUnitAbilityLevel(t, HOLD_YOUR_GROUND) > 0 and IsUnitClassified(u, "Mounted") then
                        set damage = damage - 1
                    endif
                    if GetUnitAbilityLevel(t, PARRY) > 0 and openingstrike != 3 then
                        set damage = damage - GetUnitAbilityLevel(t, PARRY)
                    endif
                    set Unit_TempHP[id2] = Unit_TempHP[id2] - damage
                    set Unit_Attacks[id] = Unit_Attacks[id] - 1
                endif
            endif
            call GroupRemoveUnit(ATTACKERS, u)
        endloop
        call GroupEnumUnitsInRect(ATTACKERS, Field[AX][AY], null)
        
        ///Normal Defenders///
        loop
        set u = FirstOfGroup(DEFENDERS)
        set t = GroupRandomUnit(ATTACKERS, true)
        exitwhen u == null
            if (GetUnitAbilityLevel(u, FIRST_STRIKE) == 0 and GetUnitAbilityLevel(u, LAST_STRIKE) == 0 and (GetUnitAbilityLevel(u, HOLD_YOUR_GROUND) == 0 or (GetUnitAbilityLevel(u, HOLD_YOUR_GROUND) > 0 and (IsUnitClassified(t, "Mounted") == false or GetUnitAbilityLevel(t, TRAMPLE) > 0)))) or (GetUnitAbilityLevel(u, FIRST_STRIKE) > 0 and GetUnitAbilityLevel(u, LAST_STRIKE) > 0 and (GetUnitAbilityLevel(u, HOLD_YOUR_GROUND) == 0 or (GetUnitAbilityLevel(u, HOLD_YOUR_GROUND) > 0 and (IsUnitClassified(t, "Mounted") == false or GetUnitAbilityLevel(t, TRAMPLE) > 0)))) or (GetUnitAbilityLevel(u, LAST_STRIKE) > 0 and GetUnitAbilityLevel(u, FIRST_STRIKE) == 0 and GetUnitAbilityLevel(u, HOLD_YOUR_GROUND) > 0 and GetUnitAbilityLevel(t, TRAMPLE) == 0 and IsUnitClassified(t, "Mounted")) then
                set id = GetUnitId(u)
                if openingstrike == 0 then
                    set openingstrike = 4
                endif
                ///The attack calculation
                if Unit_Attacks[id] > 0 then
                    set id2 = GetUnitId(t)
                    set damage = GetRandomInt(Unit_DamageMin[id], Unit_DamageMax[id]) - Unit_Armor[id2]
                    if GetUnitAbilityLevel(t, BLOCK) > 0 and openingstrike == 4 then
                        set damage = damage - GetUnitAbilityLevel(t, BLOCK)
                    endif
                    if GetUnitAbilityLevel(t, TRAMPLE) == 0 and GetUnitAbilityLevel(u, HOLD_YOUR_GROUND) > 0 then
                        set damage = damage + 1
                    endif
                    if GetUnitAbilityLevel(t, PARRY) > 0 and openingstrike != 4 then
                        set damage = damage - GetUnitAbilityLevel(t, PARRY)
                    endif
                    set Unit_TempHP[id2] = Unit_TempHP[id2] - damage
                    set Unit_Attacks[id] = Unit_Attacks[id] - 1
                endif
            endif
            call GroupRemoveUnit(DEFENDERS, u)
        endloop
        call GroupEnumUnitsInRect(DEFENDERS, Field[DX][DY], null)
        
        ///Last Strike Attackers///
        loop
        set u = FirstOfGroup(ATTACKERS)
        set t = GroupRandomUnit(DEFENDERS, true)
        exitwhen u == null
            if GetUnitAbilityLevel(u, FIRST_STRIKE) == 0 and GetUnitAbilityLevel(u, CHARGE) == 0 and GetUnitAbilityLevel(u, LAST_STRIKE) > 0 then
                set id = GetUnitId(u)
                if openingstrike == 0 then
                    set openingstrike = 5
                endif
                ///The attack calculation
                if Unit_Attacks[id] > 0 then
                    set id2 = GetUnitId(t)
                    set damage = GetRandomInt(Unit_DamageMin[id], Unit_DamageMax[id]) - Unit_Armor[id2]
                    if GetUnitAbilityLevel(t, BLOCK) > 0 and openingstrike == 5 then
                        set damage = damage - GetUnitAbilityLevel(t, BLOCK)
                    endif
                    if GetUnitAbilityLevel(u, TRAMPLE) == 0 and GetUnitAbilityLevel(t, HOLD_YOUR_GROUND) > 0 and IsUnitClassified(u, "Mounted") then
                        set damage = damage - 1
                    endif
                    if GetUnitAbilityLevel(t, PARRY) > 0 and openingstrike != 5 then
                        set damage = damage - GetUnitAbilityLevel(t, PARRY)
                    endif
                    set Unit_TempHP[id2] = Unit_TempHP[id2] - damage
                    set Unit_Attacks[id] = Unit_Attacks[id] - 1
                endif
            endif
            call GroupRemoveUnit(ATTACKERS, u)
        endloop
        call GroupEnumUnitsInRect(ATTACKERS, Field[AX][AY], null)
        
        ///Last Strike Defenders///
        loop
        set u = FirstOfGroup(DEFENDERS)
        set t = GroupRandomUnit(ATTACKERS, true)
        exitwhen u == null
            if GetUnitAbilityLevel(u, FIRST_STRIKE) == 0 and GetUnitAbilityLevel(u, LAST_STRIKE) > 0 and (GetUnitAbilityLevel(u, HOLD_YOUR_GROUND) == 0 or IsUnitClassified(t, "Mounted") == false or GetUnitAbilityLevel(t, TRAMPLE) > 0) then
                set id = GetUnitId(u)
                ///The attack calculation
                if Unit_Attacks[id] > 0 then
                    set id2 = GetUnitId(t)
                    set damage = GetRandomInt(Unit_DamageMin[id], Unit_DamageMax[id]) - Unit_Armor[id2]
                    if GetUnitAbilityLevel(t, PARRY) > 0 then
                        set damage = damage - GetUnitAbilityLevel(t, PARRY)
                    endif
                    set Unit_TempHP[id2] = Unit_TempHP[id2] - damage
                    set Unit_Attacks[id] = Unit_Attacks[id] - 1
                endif
            endif
            call GroupRemoveUnit(DEFENDERS, u)
        endloop
        call GroupEnumUnitsInRect(DEFENDERS, Field[DX][DY], null)
        
        set openingstrike = 7
    endloop

    /// Here the real combat animations begin: This is something that is not finished yet. I quickly set this up///
    
    call DisplayTextToForce( GetPlayersAll(), "Combat Animation" )
    loop
    set u = FirstOfGroup(ATTACKERS)
    exitwhen u == null
        set id = GetUnitId(u)
        if Unit_TempHP[id] < 1 then
            call RemoveItem(UnitItemInSlot(u,0))
            call KillUnit(u)
        else
            call SetUnitState(u, UNIT_STATE_LIFE, R2I(Unit_TempHP[id]))
        endif
        call GroupRemoveUnit(ATTACKERS, u)
    endloop
    loop
    set u = FirstOfGroup(DEFENDERS)
    exitwhen u == null
        set id = GetUnitId(u)
        if Unit_TempHP[id] < 1 then
            call RemoveItem(UnitItemInSlot(u,0))
            call KillUnit(u)
        else
            call SetUnitState(u, UNIT_STATE_LIFE, R2I(Unit_TempHP[id]))
        endif
        call GroupRemoveUnit(DEFENDERS, u)
    endloop
    
endfunction
endlibrary




EDIT: If there is an experienced coder that would like to see the map, I could also just send the map file over so you can check for yourself while I use certain things like using strings for the unit classified stuff.
 
Level 12
Joined
Oct 16, 2010
Messages
680
save things to variables. its not only for efficiency but readability.
with this much complexity maybe it would be better to make a struct for each unit or use a hashtable
SO attack order may vary depending on the enemy for e.g.( unit with Hyg attacked by unit wih trample or anything diff)
Then order predefinition is out of date:/
that would require you to predifene for each round which unit attacks which unit. and after that u can make the changes in the setup.

stats:
-Str
-FS
-LS
-Ch
-Bl
-Pa
-Hyg
-Tr
-currTarget (current target)
-lastAttacked (storing the unit that was attacked last by THIS unit[maybe u
want to add some modifiers that give bonus on attacking same unit twice;D])
-lastAttacker (U'll figure it out [for counter attacks])
-HP
-Atks (maximum number of attacks)

most of these should be booleans

when you set up a group you preconfigure the units
and in a fight you only need to do something like this

JASS:
    //get max str as u did before
    //loop through both groups and define targets and save them with this
        call SaveUnitHandle(CBHASH,unithHandle,someIndex,GroupRandomUnit(.......)



    //loop through attackers with fs ( I use tables here for example )
        set u = FirstOfGroup(ATTACKERS)
        exitwhen u == null

        // preload components in locals here both for the unit and its target
        // just those needed by first check

        if Atks>=CombatRound and atkrFS or (atkrHyg and not targTramp) then //if combatround is not higher then how many times the unit can attack and has FS
                // load rest of the data
                // perform dmg calc
        endif

this requires you to reconstruct the whole script I know ,but this way you will be able to manage the battle in a slightly easier way with many new options

EDIT: i merged the condition:/
 
Last edited:
Level 19
Joined
Oct 12, 2007
Messages
1,821
I see.
It sounds much easier on paper, but it is way out of my league.
My experience with hashtables and structs is really 0.
I might have to read up on those first, but I'll keep your solution in mind.
I think I really have to change things and work on this.
Thanks for the help!
 
Status
Not open for further replies.
Top