• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[vJASS] DoT spell

Status
Not open for further replies.

Chaosy

Tutorial Reviewer
Level 40
Joined
Jun 9, 2011
Messages
13,219
Code is updated, opinions?
www.hiveworkshop.com/forums/triggers-scripts-269/dot-spell-255603/index2.html#post2567452

Hello, I always thought I understood how to code spells using structs even though I never actually tried. But when I started to think about it, it wasn't so obvious. The problem I couldn't manage to solve properly was that I can't catch an struct instance afterwards.

For example, let's create a simple DoT spell that deals 50 damage every second.

I can create an instance of the struct ONCE, which allows me to set some variables and deal 50 damage ONCE. However, how do I repeat to process, there is no list that I can use to gather all the instances etc. In GUI we use unit groups mos t of the time but that doesn't work with structs. So I came up with a really silly idea.

JASS:
struct DoT
	integer i
    public static method create takes unit u returns DoT
        local DoT this = DoT.allocate()
		
        //do stuff
		loop
			exitwhen this.i == 5
			set this.i = this.i + 1
			call TriggerSleepAction(1)
			call SetUnitState(u, UNIT_STATE_LIFE, RMaxBJ(0,GetUnitState(u, UNIT_STATE_LIFE) - 50))
		endloop
		call this.deallocate()
        return this
    endmethod
endstruct

  • Untitled Trigger 002
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
    • Actions
      • Custom script: local DoT instance = DoT.create(GetTriggerUnit())

It works fine, it's MUI. (I tested with 6 units casting the same spell at the same time) But I highly doubt this is the correct way to do things.
 
Last edited:
Level 22
Joined
Sep 24, 2005
Messages
4,821
Could you clarify the question, I couldn't get it, sorry.

EDIT: Do you mean you want to loop through the struct instances?
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
I'm not sure if I remember correctly but...

JASS:
globals
  DoT array Instance
endglobals
...

  local integer i=0
  loop
    exitwhen i==max_instance
    // do something with Instance[i]
    set i=i+1
  endloop
 
JASS:
struct dot
    unit attacked
    real damage
    real duration

    private thistype prev    // these are that you don't let a gap in your periodic loop
    private thistype next
    static thistype first = 0

    public static method create takes unit u, real d, real t returns DoT
        local thistype this = allocate()

        set attacked = u
        set damage = d
        set duration = t

        if first == 0 then
           call StartTimer(yourTimer, ...)
        endif

        set next = first
        set first.prev = this
        set first = this
        set prev = 0
            
        return this
    endmethod

    method destroy takes nothing returns nothing
        call deallocate()            
        set attacked = null
         
        if this == first then
            set first = next
        endif
        set next.prev = prev
        set prev.next = next
            
        if first == 0 then
            call StopTimer(yourTimer)
        endif
            
    endmethod

    // this runs periodicly by timer call
    private static method damageUnits takes nothing returns nothing
        local thistype this = first
        loop
            exitwhen this == 0
            // Damage this.attacked with this.damage
            set this.duration = this.duration - PERIODIC_TIME

            set this = this.next
        endloop
    endmethod
 

Chaosy

Tutorial Reviewer
Level 40
Joined
Jun 9, 2011
Messages
13,219
@chobibo

well, arrays aren't dynamic unlike lists. I don't really know how to explain it.. but I will try.

If you loop through an array you loop through the 'empty' slots as well. However if you us an dynamic solution you only loop through something that only contains the 'filled' slots. In c# it's called lists, and linked list in vjass I think.

@icemanbo
First post.
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
Oh, that's what you mean... That can be avoided if you shuffle the position of the deleted data with the inserted one. Anyways, just use the data structure you're familiar with.
 
Level 14
Joined
Jun 27, 2008
Messages
1,325
@IcemanBo: Why linked list, just fill an array from bottom to top. If you remove an object which is not at the end at the list just swap it with the one at the end and delete it there.

Or just use 1 timer per instance and attach the struct to the timer via hashtable. If you dont cast the spell 1000 times per second there is no difference..
 
Icemans code is fine, linked lists are more than welcome. Not that there are a must or that I'm forcing anything, yet you will be probably playing with lists many times in the future. If not with this spell, then probably with some other snippet. You might as well check it out here to exercise a bit.

Both solutions work just fine.

Btw, I don't know what we should look at in your map. Triggers are untitled and the code is posted in op anyway. Avoid TSA within loops.
 

Chaosy

Tutorial Reviewer
Level 40
Joined
Jun 9, 2011
Messages
13,219
Bannar, it was requested I didn't upload it because it was 'needed'. And I know that you shouldn't use that, that's why I made this thread in the first place.

EDIT: How about this, I read up on the linked list but it was kinda complicated so I made a new solution based on it. Is this good enough, it can support up to around 8100 instances so I doubt it would bug.
JASS:
globals
	DoT array old
	integer size = 1
	//setup
	real loopSpeed = 0.03
	integer allowedInstances = 100
endglobals

struct DoT
	real counter
	unit u
	real interval
	real damage
	integer tics
	integer ticCount
    public static method create takes unit u, real interval, real damage, integer tics returns DoT
        local DoT this = DoT.allocate()
		
		set this.u = u
		set this.interval = interval
		set this.damage = damage
		set this.tics = tics
		
		set old [size] = this
		set size = size + 1
		if size > allowedInstances then
			set size = 1
		endif
        return this
    endmethod
	
    public method destroy takes nothing returns nothing
        call this.deallocate()
    endmethod
endstruct

function Actions takes nothing returns nothing
	local integer count = 0
	local integer i = 0
		loop
			exitwhen i > size
			if old[i] > 0 then
				set count = count + 1
				set old[i].counter = old[i].counter + loopSpeed
				if old[i].counter > old[i].interval then
					set old[i].counter = 0
					set old[i].ticCount = old[i].ticCount + 1
					call SetUnitState(old[i].u, UNIT_STATE_LIFE, RMaxBJ(0,GetUnitState(old[i].u, UNIT_STATE_LIFE) - old[i].damage))		
					if old[i].ticCount > old[i].tics then
						call old[i].destroy()
						set old[i] = 0
					endif
				endif
			endif			
			set i = i + 1
		endloop		
endfunction

//===========================================================================
function InitTrig_Struct takes nothing returns nothing
    set gg_trg_Struct = CreateTrigger(  )
    call TriggerRegisterTimerEventPeriodic( gg_trg_Struct, 0.03 )
    call TriggerAddAction( gg_trg_Struct, function Actions )
endfunction
 
Last edited:
Status
Not open for further replies.
Top