| 04-10-2008, 12:45 PM | #1 |
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. 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 |
I would use GC as long as no problems arise. If someting bad does happen I would write something like this: 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 JASS:local string s = I2S(H2I(GetTriggerUnit()))+I2S(H2I(GetSpellTargetUnit()))+"Spell Name" local data d = GetInteger(s,"struct") 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 |
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 |
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 |
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 | ||
Quote:
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:
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 | |
Quote:
|
| 04-10-2008, 09:22 PM | #8 |
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 |
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 | |
Quote:
|
| 04-11-2008, 06:22 AM | #11 | |
Quote:
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 |
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 | ||
everyone seems to think I2H is bad...anyhow Quote:
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 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 |
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 |
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 |
