HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Elimination of GC....

04-10-2008, 12:45 PM#1
emjlr3
I am going to try and make this a simple example so it does not end up taking me forever

Spell 1 - Lets call it "Stacking Long Term Damage"

You cast a spell, with a short cd, deals DoT to the target, however, a new cast by you restarts this duration, but stacks the damage. So say, cast 1, 25 DoT for 5s, you cast again after 3s, in which 15 damage should have been done, the time resets, but now you are doing 50 DoT for the next 5s, and so on. Now say hero 2 comes up, and wants his own instance of the spell. So he casts the ability on the same poor little target, in the same fashion. The target is now taking 100 DoT, at different intervals, from two seperate casters, which end at seperate times. Now this spell can be used by any number of units on this map.

The way I would have approached this in the past, or rather, in the present, is with a GC approach(the only time I use GC mind you), in a fashion as shown below.

Collapse JASS:
struct data
  unit caster
  unit target
  timer t
  integer ticks = 5
  real damage = 5.
  string storage
endstruct

function Effects takes nothing returns nothing
  local data d = GetData(GetExpiredTimer())

  if d.ticks==0 then
    call ClearTable(d.storage)
    call EndTimer(d.t)
    call d.destroy()
  else
    //damage d.target by d.caster based on d.damage
    //add some effects or whatever
    set d.ticks = d.ticks - 1
  endif
endfunction
function OnCast takes nothing returns nothing
  local string s = I2S(H2I(GetTriggerUnit()))+I2S(H2I(GetSpellTargetUnit()))+"Spell Name"
  local data d = GetInteger(s,"struct")

  if d==0 then
    set d = data.create()
    set d.caster = GetTriggerUnit()
    set d.target = GetSpellTargetUnit()
    set d.t = NewTimer()
    set d.s = s
    call SetData(d.t,d)
    call TimerStart(d.t,1.,true,function Effects)
  else
    call PauseTimer(d.t)
    set d.ticks = 5
    set d.damage = d.damage + 5.
    call TimerStart(d.t,1.,true,function Effects)
  endif
endfunction

can anyone think of how to do this w/o using GC?

remember all instances of this spell are independant, damage cannot be grouped unless you can discern who is to deal it, and all intervals/end times should be independant of one another

the only thing i could think of would be to have a timer per unit(or 1 timer, with an array of active instances)(this again would not allow for independant intervals), with an array of damages, ticks and casters(with an index to keep track of how many there are), where you store who deals what damage when the callback function is ran and how many ticks are left. On a new cast, loop through your index, if the caster is already there, update the damage, time and length, if not, add him in. I can't really say I like this method, I can't see it being any faster then the limited GC here(though maybe it is), and again, the intervals are not independant of one another, which is not desireable
04-10-2008, 03:30 PM#2
DiscipleOfLife
I would use GC as long as no problems arise. If someting bad does happen I would write something like this:
Collapse JASS:
library GetSetFlushHandleHandleInt

    private function H2I takes handle h returns integer
        return h
        return 0
    endfunction
    
    globals
        private handle array X
        private handle array Y
        private integer array Int
        private boolean array Reserved
    endglobals
    
    function FlushHandleHandleInt takes handle x, handle y returns integer
     local integer i = H2I(x) + H2I(y)
        set i = i - (i / 8191)*8191
        loop
            exitwhen X[i] == x and Y[i] == y
            set i = i + 1
            if i > 8190 then
                set i = 0
            endif
        endloop
        set Reserved[i] = false
     return Int[i]
    endfunction

    function GetHandleHandleInt takes handle x, handle y returns integer
     local integer i = H2I(x) + H2I(y)
        set i = i - (i / 8191)*8191
        loop
            exitwhen X[i] == x and Y[i] == y
            set i = i + 1
            if i > 8190 then
                set i = 0
            endif
        endloop
     return Int[i]
    endfunction
    
    function SetHandleHandleInt takes handle x, handle y, integer int returns nothing
     local integer i = H2I(x) + H2I(y)
        set i = i - (i / 8191)*8191
        loop
            exitwhen not Reserved[i]
            set i = i + 1
            if i > 8190 then
                set i = 0
            endif
        endloop
        set Reserved[i] = true
        set X[i] = x
        set Y[i] = y
        set Int[i] = int
    endfunction
    
endlibrary 

(This passes syntax check but that is as far as I went with testing this.)
Get/FlushHandleHandleInt on handles with no stored values crashes the thread. In other words this doesn't work "as is" for you, instead you'll have to change the functions to return some null value to make this work for such handles.

EDIT:
Forgot to mention that the idea would be to replace this
Collapse JASS:
local string s = I2S(H2I(GetTriggerUnit()))+I2S(H2I(GetSpellTargetUnit()))+"Spell Name"
  local data d = GetInteger(s,"struct")
with this
Collapse JASS:
    local data d = GetHandleHandleInt(GetTriggerUnit(), GetSpellTargetUnit())

Hmm I just noticed the "Spell Name" part... This solution doesn't take that into account.
04-10-2008, 04:01 PM#3
emjlr3
would need something like this specific for each spell of this type, and I think it crashes because it hit the OP Limit, that 8190 loop will do that to you
04-10-2008, 05:20 PM#4
chobibo
If CasterA casts the spell twice on a unit, effects will stack but the timer will be resetted to accomodate the latest cast? So would it only need a single timer for each caster since the spell duration resets if ever the spell is cast?
04-10-2008, 05:48 PM#5
Anitarf
As long as you have one timer per instance, you need to get the information about which instance the timer belongs to somehow, either through gamecache, looping through an array or using that bug with timer elapsed time. The only way to avoid this is by running all your instances on a single timer.

Why the heck do you need independant timers anyway? For DoT? I don't think so.
04-10-2008, 06:07 PM#6
emjlr3
Quote:
Originally Posted by chobibo
If CasterA casts the spell twice on a unit, effects will stack but the timer will be resetted to accomodate the latest cast? So would it only need a single timer for each caster since the spell duration resets if ever the spell is cast?

what if he casts it on two seperate units while they are both under the effects?

I guess you could, as i stated in the last paragraph of my first post, store an array of all units currently under the effects from the given caster, loop through those on a new cast, and either update the correct one or add a new index to the array for the new target, just seems like a whole lot more work to me, though maybe its the best method in the end

in anycase, I seem to have thought up alternatives on my own, albeit they seem more time cunsuming to code and actually run

Quote:
Originally Posted by Anitarf
Why the heck do you need independant timers anyway?

to have them run independantly of one another, and thus, the effects incurred from each

I suppose its not too big a deal to have everything done at the same time, as opposed to at individual intervals, however, it still seems to achieve that the best method, aside from what was suggested in post 2 (which has troubles itself), is my limited GC usage
04-10-2008, 06:29 PM#7
Anitarf
Quote:
Originally Posted by emjlr3
to have them run independantly of one another, and thus, the effects incurred from each
But why? I mean, unless you want to simulate general relativity...
04-10-2008, 09:22 PM#8
Captain Griffen
Why not simply attach it to a timer and have them run completely independantly? Attaching stuff to units is rarely needed.
04-10-2008, 10:23 PM#9
emjlr3
how do u get what is attached to the timer the next time its casted?

regardless, I have come to good conclusions here, thnx for the input guys
04-10-2008, 11:53 PM#10
Rising_Dusk
Quote:
Originally Posted by emjlr3
how do u get what is attached to the timer the next time its casted?
You first stop thinking in terms of attaching anything to a timer and second you loop through an array of everything you want to effect in the timer callback.
04-11-2008, 06:22 AM#11
Captain Griffen
Quote:
Originally Posted by emjlr3
how do u get what is attached to the timer the next time its casted?

regardless, I have come to good conclusions here, thnx for the input guys

You don't need to, if they are independant of each other, then they don't affect each other...
04-11-2008, 06:29 AM#12
PandaMine
If you think about it there is actually nothing really wrong with gamecache. It is the most stable form of data management and very simple to use and quite versatile with its use of strings

Its only real disadvantage is its speed, this is mainly due to the fact (tests I have done confirmed this) that its caching is done with strings instead of what is conventionally done with integers. This is what slows the system down.

GC shouldn't be labeled as some sort of demon, it is also by far the best for storing database's that don't need to be accessed incredibly fast
04-11-2008, 12:40 PM#13
emjlr3
everyone seems to think I2H is bad...anyhow

Quote:
You first stop thinking in terms of attaching anything to a timer and second you loop through an array of everything you want to effect in the timer callback.

well yea, thats the logical answer, though in this instance it becomes a little more tiresome, and where I can avoid such things I do

Quote:
You don't need to, if they are independant of each other, then they don't affect each other...

you want the instance from a caster on the given target to remain constant, not have 4 or 5 of them floating around, that is just silly
04-11-2008, 12:54 PM#14
Vexorian
You can use gamecache and still not use I2H , it is fine, if you support gamecache with structs, you only need one gamecache call to attach and one to read. So, it would be fast anyways. Even things like ABC or HAIL are barely twice as fast as gamecache, for a single call it the difference is nothing really.

You could worry about the string leak! But to be serious, a string leak after I2S, would at most take 10 more bytes out of your computer, 10 more bytes per handle that uses gamecache. It doesn't sound so terrible...
04-11-2008, 01:29 PM#15
PandaMine
Yeah unless someone plays your map for a week straight

In fact when you think about it the only real disadvantage of gamecache is that its slow, other then that it does everything it supposed to flawlessly