• 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.

saving a struct into a hashtable?

Status
Not open for further replies.
Level 13
Joined
Jan 2, 2016
Messages
978
Hmm, another question...
I did this
JASS:
    private struct SpeedList
        integer count = 0
        real array speed
    endstruct
and it asked me for array size.
Then I did real array speed [100]. What does this actually mean?
I will have 100 as a maximum for this array, or it will be using only the [100-th] value?
 
It does mean an instance has the real arrays with size 100. You may use it until 100, not only the 100th.

JASS:
this.speed[3] = 5 // valid
this.speed[22] = 5 // valid


However. Using arrays as non-static member limites the usage of structs.
structs work with integer array. A struct (instance) will get an unique intger from 1 to 8191. (which is the limit for arrays)
so..

JASS:
real[ID] = 3
this.real = 3
^The this keyword is similar like the ID above.
It defines the position of the array, but with a differnt syntax.

Now this also means if we use an array as non-static struct member, our usage of the struct array gets limited.
We can't use 8191 instances anymore but, but only 8191/arraysize instead.

In your example you can use 8191/100 instances -> 81.

In some cases we still might use it, because it's simple. But if the array goes too big
we're used to use an oder struct as member instead of the array. Or a list/stack/what ever.

JASS:
struct list
    real speed
endstruct

struct main
    integer count
    list data
endstruct

So the main struct can now use 8191 indices now for the speed of each instance.
 
Level 13
Joined
Jan 2, 2016
Messages
978
genious!

and what about the syntax?
JASS:
local integer i = 1
local main A = main.create()
loop
    exitwhen i > 10
    set A.count = A.count + 1
    set A.data[A.count] = list.create()
    set A.data[A.count] = 10.00*I2R(i)
    set i = i + 1
endloop
is that how it would look like?
or would I need to use methods? If so, how would the method look like?

EDIT: My 1-st idea didn't work, so I did this:
JASS:
    private struct list
        real speed = 0.00
    endstruct

    private struct SpeedList
        integer count = 0
        list data
        
        method fill takes real speed returns list
            local list A = list.create()
            set A.speed = speed
            return A
        endmethod
    endstruct

    function test takes nothing returns nothing
        local integer i = 1
        local SpeedList A = SpeedList.create()
        loop
            exitwhen i > 10
            set A.count = A.count + 1
            set A.data = A.fill(10*I2R(i))
            set i = i+1
        endloop
    endfunction

The problem is that I don't have any idea how to link each speed value (in the list struct) to the count value (in the SpeedList struct)...
 
Hm, no. And I did not tell very good now I think..

You need (only) create the head of the list and then handle the list logic yourself in the list struct, or with a system. (add/remove)

In your example data is treaten like an array, which is wrong. It's not an array. That is exactly what we try to avoid.

Also set A.data[A.count]here no member of type [real is references, so it can't be right.
It must be something like something.speed at end, so it specifies the "speed" member of an instance.

I would re-think if one instance really need 100 speed reals.
And maybe you can anyways first write it with array as struct member,
to get more used the normal syntax first.
But if a list of reals is really needed for each instance then you can search for list examples first and so on.
Trial and error.
 
Level 13
Joined
Jan 2, 2016
Messages
978
Okay, after some thinking, I think that this is the way to do it...
JASS:
    private struct list
        real array speed [100]
    endstruct

    private struct SpeedList
        integer count = 0
        list data
    endstruct

    function test takes nothing returns nothing
        local integer i = 1
        local SpeedList A = SpeedList.create()
        set A.data = list.create()
        loop
            exitwhen i > 10
            set A.count = A.count + 1
            set A.data.speed[A.count] = 10*I2R(i)
            set i = i+1
        endloop
    endfunction

Doesn't give me syntax errors :p
What do you think?

EDIT: I added another loop after the 1-st one
JASS:
        loop
            exitwhen i <= 1
            call BJDebugMsg(R2S(A.data.speed[A.count]))
            set A.count = A.count - 1
            set i = i - 1
        endloop
It seems to work :D
 
Level 13
Joined
Jan 2, 2016
Messages
978
And would it work like this?:
JASS:
    private struct list extends array
        real array speed [100]
    endstruct

    private struct SpeedList
        integer count = 0
        list /* not sure if I need to write "array" here */ data [8190]
    endstruct

EDIT: Okay, I got an error when I tried this x_x
It suggests me to use dynamic array, but I don't know how to do that xP
 
I think one should try around first what goes and what not.

Get used to normal struct usage without lists first I suggest.
Try to understand how to use an array in struct and what it means.
Try to figure out what it means to have an other struct as member of a struct.

The experiences won't come from alone, but are needed to get better and to understand. Step by step. :)
 

Chaosy

Tutorial Reviewer
Level 41
Joined
Jun 9, 2011
Messages
13,241
list data = list.create()
data.speed[0] = something

at least that's how you use lists most of the time.

edit: example from the vjass manual.
Sometimes, you'd like to have a global array of a struct type, just to be able to have that field syntax we all like so much, it can be more complicated than it is supposed to, for example you have to manually initialize all the indexes to create the unique indexes, etc. Another issue is when you do not really want to use .allocate() and .destroy() you would like to have your own ways for allocation. Array structs are a small syntax enhancement that is equivalent to an array of a struct type, you would be able to use the members for each index and you will not have to worry about .create().
JASS:
//Array structs are hard to explain, but should be simple to understand with an example

struct playerdata extends array //syntax to declare an array struct
    integer a
    integer b
    integer c
endstruct

function init takes nothing returns nothing
 local playerdata pd

    set playerdata[3].a=12  //modifying player 3's fields.
    set playerdata[3].b=34  //notice it behaves as a global array
    set playerdata[3].c=500

    set pd=playerdata[4]
    set pd.a=17             //modifying player 4's fields.
    set pd.b=111            //yep, this is also valid
    set pd.c=501
endfunction

function updatePlayerStuff takes player p returns nothing
 local integer i=GetPlayerId(p)

    //some random function.
    set playerdata[i].a=playerdata[i].b

endfunction
 
Level 7
Joined
Oct 19, 2015
Messages
286
First of all, do you have an upper limit on the number of speeds that can be in a speedlist? Do you have an upper limit on the number of speedlists you'll ever make? If the answer to both is yes and if the product of those limits is less than 8190, then you can easily use structs with array members for this purpose.

If the answer is no, however, then there is a follow-up question: will the total number of speeds listed in all speed lists exceed 8190? If yes, then arrays (and therefore structs) are not a suitable solution for your problem, you'd need to use a hashtable.

If you find yourself somewhere in between these two extremes, then a list-based solution can be used.

The idea of a list is that your main struct doesn't have to store all the values like it would if you used an array member, but instead just stores the first node of a list; that node then stores the first value as well as a link to the next node, which stores the next value and so on. That way, each struct instance stores a link to only one other struct instance, so you don't have to use arrays.

The advantage of this approach is that it scales well. With arrays, you must size them according to the largest possible list, so you end up wasting storage space when lists are shorter. With a linked list approach, you only use as much storage space as you actually have items on your list.

The disadvantage of linked lists is that you don't have an efficient method of random access, if you want to find a specific value you need to loop through the list searching for it, so sometimes even with a low number of instances using a hashtable is preferable. However, linked lists are still a very useful data structure.

Most often, we use a double linked list, which means that each node is aware of both the next and the previous node in a list. That way, if we obtain a node by some means other than by looping through the list, we can remove it from the list without having to loop through the list first in order to properly re-link the other nodes.
 
Level 13
Joined
Jan 2, 2016
Messages
978
The upper limit on the speeds will probobly be around 32 (64 at most). But I kind a need this knowledge for a system, so I can't be sure if it will be enough for other people, using the system.
As for the number of speedlists - it should be lower than 8000 for sure. Probobly less than 1000 too (unless someone tries to purposely break my system :D)

I'm kind a already using a hashtable for my system, but I need 3 "keys" instead of 2.
1 for the unit, 1 for an ability id, and 1 for the speed (the count is playing the role of the 3-rd key).

My problem is that I haven't used structs before and I'm not really familiar with them.
I will really apreciate if someone writes an example code with "linked lists".

And the example Chaosy wrote... do integer a, b and c act like 3x global integer arrays, or are they like 'local' integer arrays?
 
Level 7
Joined
Oct 19, 2015
Messages
286
For an example, I would need to know what your system is supposed to do. The structure of your data should always be tailored to how you use that data. So, I require a brief summary of what you are trying to accomplish.
 
Level 13
Joined
Jan 2, 2016
Messages
978
Well, check the last link in my signature ( the Movespeed modifying library ).
I need this for the "ChangeUnitSpeed" and "OnExpire" functions. The rest are okay.
So.. at the moment I'm dynamically assigning hashtables to units as "1-st key", then I use the buff_ability (id) as 2-nd key (parent key in the hashtable), in the '0' - I'm saving the count, and in the next fields I'm saving the speed values.

However, assigning hashtables dynamically isn't very efficient, and I'm limited to 250 units having their speed modified at once (with the ChangeUnitSpeed function), so I just want to save a struct in a fixed hashtable, and load the speeds from it.
 
Level 7
Joined
Oct 19, 2015
Messages
286
I don't know why you are linking them to abilities, I don't have the time to read the code in more detail at the moment but it seems to me that all speed modifiers could be the same as far as this system is concerned. Then, it would be up to the code that uses this library to do any linking as needed.

I don't have the time to write any longer code right now so I'll just post a very similar library that I wrote some time ago that demonstrates how I would approach this problem. Instead of modifying unit speed, it modifies colour, but the underlying principles are the same.

JASS:
//! zinc

library ColorModifiers requires ARGB, Table
{

    public constant integer COLOR_MODIFIER_OVERLAY = 1;
    public constant integer COLOR_MODIFIER_MULTIPLY = 2;
    public constant integer COLOR_MODIFIER_ADDITIVE = 3;

    public struct ColorModifier
    {
        private unit target;
        private ARGB color;
        private integer order;
        
        private integer mode = COLOR_MODIFIER_OVERLAY;
        private real factor= 1.0;
        
        private thistype next, prev; // Double linked circular list.
        
        private static Table table; // For attaching modifier lists to units.
        private integer tableId;
        private static method onInit()
        { table = Table.create(); }

        method updateUnit()
        {
            ARGB unitColor = 0xFFFFFFFF;
            real a = unitColor.alpha/255.0;
            real r = unitColor.red/255.0;
            real g = unitColor.green/255.0;
            real b = unitColor.blue/255.0;

            thistype that = thistype(table[tableId]);

            this = that.next;
            while (this!=that) { // keep looping until we come around to the sentinel node again
                if (mode==COLOR_MODIFIER_OVERLAY) {
                    a = a*(factor-1.0) + color.alpha/255.0*factor;
                    r = r*(factor-1.0) + color.red/255.0*factor;
                    g = g*(factor-1.0) + color.green/255.0*factor;
                    b = b*(factor-1.0) + color.blue/255.0*factor;
                } else if (mode==COLOR_MODIFIER_MULTIPLY) {
                    a = a * color.alpha/255.0*factor;
                    r = r * color.red/255.0*factor;
                    g = g * color.green/255.0*factor;
                    b = b * color.blue/255.0*factor;
                } else if (mode==COLOR_MODIFIER_ADDITIVE) {
                    a = a + color.alpha/255.0*factor;
                    r = r + color.red/255.0*factor;
                    g = g + color.green/255.0*factor;
                    b = b + color.blue/255.0*factor;
                }

                this = this.next;
            }

            unitColor.recolorUnit( target );
        }

        private method unitListAdd() // only called on an unlisted modifier
        {
            thistype that = thistype(table[tableId]); // get the modifier list for this unit
            if (that==0) { // no existing list, create a sentinel node (the start of our list)
                that = allocate();
                table[tableId] = that; // link the sentinel node to the unit
                that.tableId = tableId;
                that.next = that;
                that.prev = that;
            } else { // other modifiers exist, find insertion point
                while (that.prev.order>this.order&&that.prev.target!=null) that = that.prev;
            }
            this.prev = that.prev; // Insert the modifier into the list.
            this.next = that;
            that.prev.next = this;
            that.prev = this;

            updateUnit();
        }
        
        private method unitListRemove() // only called on a listed modifier
        {
            if (prev==next) { // if this is the last modifier in the list then destroy the list
                table.flush(tableId); // unlink the list from the unit
                prev.deallocate(); // also destroy the sentinel node
            }
            next.prev = prev; // Remove the modifier from the list.
            prev.next = next;
            next = 0; prev = 0;
        }

        static method create( unit u, ARGB c, integer priority ) -> thistype
        {
            thistype this = allocate();
            target = u;
            color = c;
            order = priority;
            tableId = GetHandleId( u );
            unitListAdd();
            return this;
        }

        method destroy()
        {
            unitListRemove();
            target = null;
            deallocate();
        }
    }

}

//! endzinc
 
Level 13
Joined
Jan 2, 2016
Messages
978
I link the speed to abilities, cuz for example:
A unit is slowed by ability '1' for 20%, and then it's slowed by ability '2' for 80%, when ability '1' is over, I don't want to give the unit 80% speed back, I want to give it 20%.
And 1 more thing (why I'm not doing it by just saving the slow/boost amount into the ability's field in the hashtable) - When the minimum speed is reached, if the unit keeps getting slowed - its speed wouldn't be dropping, but the times the unit will get its speed increased will remain. So in the end - the unit will end up with more speed than it initially had.

I was thinking to make the Minimum speed something like 0.0001, so the system would be able to deal with ~20-30 slow stacks at once, but that would've worked if I had only hyperbolic slowing.
Since I have a linear slowing option too - this wouldn't work without seperating the linear version from the hyperbolic one, or adding many ifs. xP

And well, I'm kind a too tired to understand your trigger now :D
I'll try again tomorrow :p
 
Level 7
Joined
Oct 19, 2015
Messages
286
I link the speed to abilities, cuz for example:
A unit is slowed by ability '1' for 20%, and then it's slowed by ability '2' for 80%, when ability '1' is over, I don't want to give the unit 80% speed back, I want to give it 20%.
Well, obviously, but like I said, this can be done by the code for those abilities. For example, you have a triggered spell that slows units in an area. How I would do it would be to apply a triggered buff to all units in the area when the spell is cast and in the code for that buff, create a speed modifier when the buff is applied and store it to the buff, then remove that modifier when the buff is removed. The speed modifier doesn't need to know which spell applied it because it's the spell code that's keeping track of the modifier, not the other way around.

The idea is to write a system that does one specific thing: proper handling of the stacking of speed modifiers. You should focus on that, don't also put a buff/spell system on top of it. If users want their speed modifiers to be applied by a buff/spell, they can use another system for that part.
 
Level 13
Joined
Jan 2, 2016
Messages
978
Well, obviously, but like I said, this can be done by the code for those abilities. For example, you have a triggered spell that slows units in an area. How I would do it would be to apply a triggered buff to all units in the area when the spell is cast and in the code for that buff, create a speed modifier when the buff is applied and store it to the buff, then remove that modifier when the buff is removed. The speed modifier doesn't need to know which spell applied it because it's the spell code that's keeping track of the modifier, not the other way around.

The idea is to write a system that does one specific thing: proper handling of the stacking of speed modifiers. You should focus on that, don't also put a buff/spell system on top of it. If users want their speed modifiers to be applied by a buff/spell, they can use another system for that part.

Well, I AM saving the speed modifier into the buff's ID. I call it "buff_ability" cuz it's an item ability, applying the buff. And I used to have a "buff_id" variable, which was the id of the buff applied, but later I removed that, but kept calling the ability, giving the buff "buff_ability".
I do that, cuz 2 different item abilities may give the same buff, and if something like this happens - it will cause malfunction in the script (if I was saving the modifier in the buff-id instead).

And well, My initial idea was to make my slows dispellable, until it turned out that item ability buffs / tornado aura buffs can't be dispelled.
Then I reworked how my system works, but kept adding buffs xP
 
Level 13
Joined
Jan 2, 2016
Messages
978
Well, today I still don't understand much of your script. But I see that you are using 'Table' as resource.
Anyways.. when you don't know what to do - do what you know.
What do you think about my simple, but stupid idea?:
JASS:
    private struct SpeedList
        integer count = 0
        real speed1 = 0.00
        real speed2 = 0.00
        real speed3 = 0.00
        real speed4 = 0.00
        real speed5 = 0.00
        real speed6 = 0.00
        real speed7 = 0.00
        real speed8 = 0.00
        real speed9 = 0.00
        real speed10 = 0.00
        real speed11 = 0.00
        real speed12 = 0.00
        real speed13 = 0.00
        real speed14 = 0.00
        real speed15 = 0.00
        real speed16 = 0.00
        real speed17 = 0.00
        real speed18 = 0.00
        real speed19 = 0.00
        real speed20 = 0.00
        real speed21 = 0.00
        real speed22 = 0.00
        real speed23 = 0.00
        real speed24 = 0.00
        real speed25 = 0.00
        real speed26 = 0.00
        real speed27 = 0.00
        real speed28 = 0.00
        real speed29 = 0.00
        real speed30 = 0.00
        real speed31 = 0.00
        real speed32 = 0.00
        
        method fillSpeed takes real speed returns nothing
            set this.count = this.count + 1
            if this.count == 1 then
                set this.speed1 = speed
            elseif this.count == 2 then
                set this.speed2 = speed
            elseif this.count == 3 then
                set this.speed3 = speed
            elseif this.count == 4 then
                set this.speed4 = speed
            elseif this.count == 5 then
                set this.speed5 = speed
            elseif this.count == 6 then
                set this.speed6 = speed
            elseif this.count == 7 then
                set this.speed7 = speed
            elseif this.count == 8 then
                set this.speed8 = speed
            elseif this.count == 9 then
                set this.speed9 = speed
            elseif this.count == 10 then
                set this.speed10 = speed
            elseif this.count == 11 then
                set this.speed11 = speed
            elseif this.count == 12 then
                set this.speed12 = speed
            elseif this.count == 13 then
                set this.speed13 = speed
            elseif this.count == 14 then
                set this.speed14 = speed
            elseif this.count == 15 then
                set this.speed15 = speed
            elseif this.count == 16 then
                set this.speed16 = speed
            elseif this.count == 17 then
                set this.speed17 = speed
            elseif this.count == 18 then
                set this.speed18 = speed
            elseif this.count == 19 then
                set this.speed19 = speed
            elseif this.count == 20 then
                set this.speed20 = speed
            elseif this.count == 21 then
                set this.speed21 = speed
            elseif this.count == 22 then
                set this.speed22 = speed
            elseif this.count == 23 then
                set this.speed23 = speed
            elseif this.count == 24 then
                set this.speed24 = speed
            elseif this.count == 25 then
                set this.speed25 = speed
            elseif this.count == 26 then
                set this.speed26 = speed
            elseif this.count == 27 then
                set this.speed27 = speed
            elseif this.count == 28 then
                set this.speed28 = speed
            elseif this.count == 29 then
                set this.speed29 = speed
            elseif this.count == 30 then
                set this.speed30 = speed
            elseif this.count == 31 then
                set this.speed31 = speed
            elseif this.count == 32 then
                set this.speed32 = speed
            endif
        endmethod
        
        method getSpeed takes nothing returns real
            set this.count = this.count - 1
            if this.count == 0 then
                return this.speed1
            elseif this.count == 1 then
                return this.speed2
            elseif this.count == 2 then
                return this.speed3
            elseif this.count == 3 then
                return this.speed4
            elseif this.count == 4 then
                return this.speed5
            elseif this.count == 5 then
                return this.speed6
            elseif this.count == 6 then
                return this.speed7
            elseif this.count == 7 then
                return this.speed8
            elseif this.count == 8 then
                return this.speed9
            elseif this.count == 9 then
                return this.speed10
            elseif this.count == 10 then
                return this.speed11
            elseif this.count == 11 then
                return this.speed12
            elseif this.count == 12 then
                return this.speed13
            elseif this.count == 13 then
                return this.speed14
            elseif this.count == 14 then
                return this.speed15
            elseif this.count == 15 then
                return this.speed16
            elseif this.count == 16 then
                return this.speed17
            elseif this.count == 17 then
                return this.speed18
            elseif this.count == 18 then
                return this.speed19
            elseif this.count == 19 then
                return this.speed20
            elseif this.count == 20 then
                return this.speed21
            elseif this.count == 21 then
                return this.speed22
            elseif this.count == 22 then
                return this.speed23
            elseif this.count == 23 then
                return this.speed24
            elseif this.count == 24 then
                return this.speed25
            elseif this.count == 25 then
                return this.speed26
            elseif this.count == 26 then
                return this.speed27
            elseif this.count == 27 then
                return this.speed28
            elseif this.count == 28 then
                return this.speed29
            elseif this.count == 29 then
                return this.speed30
            elseif this.count == 30 then
                return this.speed31
            elseif this.count == 31 then
                return this.speed32
            endif
            return 1.00
        endmethod

    endstruct

EDIT: Okay, I tested it and it seems to be working just fine.
I will be limited to 32 stacks of slow at once, but will this be limited by how many units it can slow at once?

EDIT 2: I tested it by stacking over 9000 slows at once (4 types of slow, 32 times each, on 72 units), and the game didn't crash, and when the slow was over - the units had their normal speed returned.

The map didn't even lag as much as I imagined when I applied these 9000 slows at once :D - just about 0.05 sec screen freeze :p
 
Last edited:
Level 13
Joined
Jan 2, 2016
Messages
978
Well, have no fear.
While I was thinking of a way how to make the system work better (was trying to fix a pseudo-bug), I thought of a way how to stop needing this list in general.

Really took me a lot of thinking and testing tho :p
Now the system has another drawback (which I'll probobly fix later), but it's much better in general :)
 
Level 14
Joined
Nov 18, 2007
Messages
816
JASS:
library MoveSpeed uses AutoIndex

private keyword mNext
private keyword mPrev

private struct UnitSpeed
    private real mSpeed
    private UnitSpeedChange mHead
    
    private method onCreate takes nothing returns nothing
        set mSpeed = GetUnitDefaultMoveSpeed(me)
    endmethod
    
    implement AutoCreate
    
    private method recalculateSpeed takes nothing returns nothing
        local real speed = mSpeed
        local UnitSpeedChange change = mHead
        
        loop
            exitwhen change == 0
            set speed = change.apply(speed)
            set change = change.mNext
        endloop
        
        call SetUnitMoveSpeed(me, speed)
    endmethod
    
    public method push takes UnitSpeedChange change returns nothing
        set change.mNext = mHead
        set change.mPrev = 0
        if mHead != 0 then
            set mHead.mPrev = change
        endif
        set mHead = change
        
        call recalculateSpeed()
    endmethod
    
    public method remove takes UnitSpeedChange change returns nothing
        if change.mPrev != 0 then
            set change.mPrev.mNext = change.mNext
        endif
        if change.mNext != 0 then
            set change.mNext.mPrev = change.mPrev
        endif
        if change == mHead then
            set mHead = change.mNext
        endif
        
        call recalculateSpeed()
    endmethod
endstruct

struct UnitSpeedChange
    private UnitSpeed mUnitSpeed
    private real mChange
    private boolean mIsRelative
    
    thistype mNext
    thistype mPrev
    
    method apply takes real inputSpeed returns real
        if mIsRelative then
            return inputSpeed * mChange
        endif
        return inputSpeed + mChange
    endmethod
    
    public static method create takes unit which, real change, boolean isRelative returns thistype
        local thistype this = thistype.allocate()
        set this.mUnitSpeed = UnitSpeed[which]
        set this.mChange = change
        set this.mIsRelative = isRelative
        
        call this.mUnitSpeed.push(this)
        
        return this
    endmethod

    private method onDestroy takes nothing returns nothing
        call this.mUnitSpeed.remove(this)
    endmethod
endstruct
    
endlibrary

This is how i would start. By all means, add more convenience functions around it, but this seems like the bare minimum that i would work with.

AutoIndex can be found here.

EDIT:
Well, this probably wont work quite right when mixing absolute and relative changes. Ill edit again when i have code that behaves correctly.

EDIT:
Done. Should now work when mixing absolute and relative movement speed changes. Now uses the approach Anitarf suggested (a linked list of changes).

EDIT: Cleaning up the code to separate responsibilities.
EDIT: Bugfix :D
 
Last edited:
Level 13
Joined
Jan 2, 2016
Messages
978
Yeah..... I already made my system and it's working.
It's not as simple as you present it to be :p

With your library:
Slow a unit by with 300 speed by 20% - 240 speed remaining.
Then add to the unit 60 speed - 300 remaining.
Then return the 20% - (300/0.8) = 375 speed remaining.
Then take away the 60 speed you gave it = 315 speed.

I have 2 structs in my system, and each has a value for the linear changes and the hyperbolic changes.
1 of the structs tracks the overal speed of the unit (from all the speed modifications done to it), and the other keeps track of the changes done to the unit's speed from a single modifier.
That's how I made it work flawlessly :p

However, I admit that there is something to learn from the script you posted :)
 
Level 13
Joined
Jan 2, 2016
Messages
978
Okay, I admit that I'm still not so familiar with structs syntax. I got a bit confused while reading your code, so I decided to just test it instead.
When I put AutoIndex on the map - I got an error, cuz it's using LUA, and we've established that I'm 1 of the chosen ones, who can't use LUA - I get errors.

This is the code that I'm using:
JASS:
    private struct CurrentSpeed
        integer count = 0
        real default = 0.00
        real speed = 0.00
        real hyperbolic = 10000.00
        real linear = 1.00
       
        static method create takes real d returns CurrentSpeed
            local CurrentSpeed c = CurrentSpeed.allocate()
            set c.default = d
            set c.speed = d
            return c
        endmethod

        method getActualSpeed takes nothing returns real
            local real r = this.hyperbolic*this.linear
            if r < MinMoveSpeed*100.00 then
                return MinMoveSpeed
            endif
            return (r*this.default)/10000.00
        endmethod
    endstruct

    private struct Data
        unit u
        timer t
        integer buff_ab
        real linear = 0.00
        real hyperbolic = 1.00

        static method create takes unit u, integer i returns Data
            local Data d = Data.allocate()
            set d.t = CreateTimer()
            set d.u = u
            set d.buff_ab = i
            return d
        endmethod

        method onDestroy takes nothing returns nothing
            call FlushChildHashtable( Table, GetHandleId(this.t) )
            call DestroyTimer(this.t)
        endmethod

        method getEndSpeed takes real modif, CurrentSpeed c, boolean b returns real
            local real result
            if b then
                set result = c.speed - c.default*(1-modif)
                set this.linear = this.linear + (modif - 1)
                set c.linear = c.linear + (modif - 1)
            else
                set result = c.speed * modif
                set this.hyperbolic = this.hyperbolic * modif
                set c.hyperbolic = c.hyperbolic * modif
            endif
            if result < MinMoveSpeed then
                return MinMoveSpeed
            endif
            return result
        endmethod

        method getCurrentSpeed takes CurrentSpeed c returns real
            set c.linear = c.linear - this.linear
            set c.hyperbolic = c.hyperbolic / this.hyperbolic
            return c.getActualSpeed()
        endmethod
    endstruct

You will notice how it has some extra stuff (like a timer and an integer called "buff_ab", but they are used for the rest of the system (which I'm not showing), otherwise my way of doing things is kind a simpler than yours, and it doesn't require external libraries.
However, I will try to rework it to fill the fields of these structs with the "create" method, like you are doing in yours. I kind a like this idea :p

You will also notice that even for the 'linear' change I'm using %s, instead of fixed values.
I simply calculate what % of the unit's speed equal to the desired value in the functions, which aren't shown.

And the value of CurrentSpeed hyperbolic is 10000.00 instead of 1.00, cuz I wasn't sure if I keep dividing it by some value - wouldn't it get too small, and get some error - like presenting 0.0000139 like 0.000014... I simply don't know with what accuracy do these reals work with.

EDIT: I applied the changes - filling some of the fields upon "create" :p
However, I wouldn't merge the 'create' method with 'getUnitSpeed' method, cuz even if I'm always changing the unit's speed upon creating the "Data", I'm not always creating the "Data", when I'm changing the unit's speed. And if I add the 'getCurrentSpeed' to "Data", then I'd need to add an if in my "working" function. So I'll just keep it like this.

EDIT 2: By the way, how does your system handle inbuild slows or movespeed changes from items?
I had a bit of trouble with them in the begining, so I'm curious if your system solved that problem from the start. (The 1-st version of the library you posted wasn't really capeable of handling speed changes from items/normal abilities)
 
Last edited:
Level 7
Joined
Oct 19, 2015
Messages
286
I don't think it's really possible to handle ability and item slows. Whenever the system updates the unit's speed, it can check if the unit's current speed is different from what it was set to last time, so it's possible to detect changes from outside sources, but there's no way of knowing whether that change was relative or absolute, so you have no way of knowing how to account for it.

With a system like this in place, you should be coding all your speed modifiers anyway, so that's not such a big issue.
 
Level 13
Joined
Jan 2, 2016
Messages
978
Umm.... My system doesn't care about "external" speed changes.
It will work, even if the unit takes "Boots of speed" while it's having its speed modified.

You should check it out (last link in my signature) :p
There is a smaple map.

You could even set the "MinMoveSpeed" to something like "150", have the gameplay constant set to 50, and this way you can slow units to 150 speed with the system, and from there to 50 with inbuilt slows :p
 
Status
Not open for further replies.
Top