Ashes of Stratholme

Take on the role of Istar, a Kirin Tor mage sent to the front lines to face the shadow devouring Lordaeron. Set out for Stratholme, where whispers of a mysterious plague have manifested into a grim reality. Dive into a classic RPG experience, leveling up your hero and looting unique gear to withstand the onslaught of infinite undead legions.

Followers
42
Downloads
0
Views
4,470
Start date
Last updated
Rating
0.00 star(s)
AoS16.png

AoS16.png

  • Media owner Shadow44
  • Date added Jan 30, 2026
  • Reaction score 1
  • Comments 0
AoS15.png

AoS15.png

  • Media owner Shadow44
  • Date added Jan 30, 2026
  • Reaction score 2
  • Comments 0
AoS14.png

AoS14.png

  • Media owner Shadow44
  • Date added Jan 30, 2026
  • Reaction score 3
  • Comments 0
AoS13.png

AoS13.png

  • Media owner Shadow44
  • Date added Jan 30, 2026
  • Reaction score 1
  • Comments 0
AoS12.png

AoS12.png

  • Media owner Shadow44
  • Date added Jan 30, 2026
  • Reaction score 0
  • Comments 0
AoS11.png

AoS11.png

  • Media owner Shadow44
  • Date added Jan 30, 2026
  • Reaction score 2
  • Comments 0
AoS10.png

AoS10.png

  • Media owner Shadow44
  • Date added Jan 30, 2026
  • Reaction score 1
  • Comments 0
AoS09.png

AoS09.png

  • Media owner Shadow44
  • Date added Jan 30, 2026
  • Reaction score 0
  • Comments 0
AoS08.png

AoS08.png

  • Media owner Shadow44
  • Date added Jan 30, 2026
  • Reaction score 1
  • Comments 0
AoS07.png

AoS07.png

  • Media owner Shadow44
  • Date added Jan 30, 2026
  • Reaction score 0
  • Comments 0
AoS06.png

AoS06.png

  • Media owner Shadow44
  • Date added Jan 30, 2026
  • Reaction score 1
  • Comments 0
AoS05.png

AoS05.png

  • Media owner Shadow44
  • Date added Jan 30, 2026
  • Reaction score 1
  • Comments 0
AoS04.png

AoS04.png

  • Media owner Shadow44
  • Date added Jan 30, 2026
  • Reaction score 0
  • Comments 0
AoS03.png

AoS03.png

  • Media owner Shadow44
  • Date added Jan 30, 2026
  • Reaction score 0
  • Comments 0
AoS02.png

AoS02.png

  • Media owner Shadow44
  • Date added Jan 30, 2026
  • Reaction score 2
  • Comments 0
AoS01.png

AoS01.png

  • Media owner Shadow44
  • Date added Jan 30, 2026
  • Reaction score 3
  • Comments 0

The Overwrite Madness

Everything started with the fact that when I played WoW, I always liked that simple idea of enemies moving around the map — some wandering randomly, others following certain paths. When I began creating my campaign, I wanted to simulate something similar, and that’s where the problem with the default enemy AI showed up. How do you tell a unit that it’s in combat and should ignore the script that makes it wander randomly around the region…

So maybe I overthought it, and there’s a much better way to do what I want, but I ended up disabled the default enemy behavior by setting their classification to Worker and turning off the fleeing options. On top of that, in the game constants I set Call for Help to 0 and gave Guard Distance an absurdly high value.

Everything looked great — except now the unit doesn’t participate in combat at all :D

So after a few more iterations I ended up overriding the default enemy behavior. At the start of the map, the whole area gets split into regions, and each region knows about its neighbors — so up to 8 of them. Whenever a unit enters a region, a dynamic event fires, the unit gets assigned to the group that matches that region’s ID, and it’s removed from the previous group it belonged to.

1779961305922.webp


Now, every 0.05 seconds the script cycles through X regions (some of them are “asleep” if nothing is happening there) and checks whether a fight can start. It takes the group of units in that region plus the groups from neighboring regions, and for each unit it tries to find the best possible enemy target. If a unit finds a target, it also has a script that lets it call for help.

Potential opponent collection script for region id:

JASS:
static method Gather takes integer id, Unit hero returns integer
        local unit enumUnit = null

        local integer size = 0
        local integer i = 0
        local integer j = 0
        local integer k = 0
        local integer neighborId = 0

        local Unit u = 0

        if SkipEnemyDetection[id] and heroIsNotCloseToArea(id, hero) then
            return 0
        endif

        loop
            set enumUnit = BlzGroupUnitAt(CombatGroups[id], i)
            exitwhen enumUnit == null

            set u = Unit.findUnit(enumUnit)
            if u != 0 and u.isAlive and BlzIsUnitInvulnerable(u.unit) == false and u.isInAction == false then
                set CombatDetectionGroup[size] = u
                set size = size + 1
            endif

            set enumUnit = null
            set u = 0
                set i = i + 1
            endloop

        if size == 0 then
            return size
        endif

            loop
                exitwhen j >= NeighborCount[id]
     
            set k = 0
                set neighborId = Neighbors[id][j]
            set j = j + 1

            loop 
                set enumUnit = BlzGroupUnitAt(CombatGroups[neighborId], k)
                exitwhen enumUnit == null

                set u = Unit.findUnit(enumUnit)
                if u != 0 and u.isAlive and BlzIsUnitInvulnerable(u.unit) == false and u.isInAction == false then
                    set CombatDetectionGroup[size] = u
                    set size = size + 1
                endif

                set enumUnit = null
                set u = 0
                set k = k + 1
            endloop
            endloop

        set hero = 0
        return size
    endmethod

So what can I actually do with this system? Well, every unit has its own:
  • enemy detection range
  • chase range
  • maximum combat duration
  • call‑for‑help range
Thanks to this system, I can detect when a unit finishes combat, tell it to return to its original position — or, in the case of patrols, to its last checkpoint — restore its full health, and make it temporarily invulnerable while it’s walking back.

Fight ending script:

JASS:
    static method leaveCombat takes nothing returns nothing
        local thistype node = base.next
        local boolean isAlive = false
        local boolean inCombat = false

                loop                
                    exitwhen node == base

            set isAlive = node.unit.isAlive
            set inCombat = node.unit.isInCombat

            if udg_CinematicOn == false and node.unit.isInCombat then
                set node.unit.increaseInCombatTime = 0.05
                set node.unit.decreaseRemainingInCombatTime = 0.05
            endif

            if(node.unit.unit == udg_Hero and isAlive and inCombat and node.fightIsOver) then                
                set node.unit.state = Unit.UNIT_STATE_OUTSITE_COMBAT
                call node.clear()

                set udg_InCombatUnit = udg_Hero
                set udg_InCombatEvent = 2.00
                set udg_InCombatEvent = 0.00
                set udg_InCombatUnit = null
            endif

            if(isAlive and inCombat and node.unit.isPlayerUnit == false and (node.outOfRange or node.fightIsOver)) then                    
                call node.unit.orderToReturn()
            endif                                                            
                            
                    set node = node.next
                endloop
    endmethod

I can create more or less aggressive units. For example, zombies that only attack when you get close, but have a huge call‑for‑help range, so others will rush in as soon as one of them spots you.
 
Back
Top