• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Rune System

  • Like
Reactions: xxdingo93xx
That's a rune system that works like the one DotA. It creates a rune at an random region if there isn't already one on the map.

You have a struct which does all the work. All you do is to add the runes and spawnrects, after that specify the interval. Then you're done.

Update:
  • Some minor changes

API:
JASS:
Runes. >>
    create()
    add( integer Item ID )
    remove( integer Item ID )
    addSpawn( rect Rect )
    removeSpawn( rect Rect )
    startTimer( real Interval )
    list()
    spawnList()

    InitRunes takes nothing returns Runes
    AddRune takes Runes d, integer RuneID returns nothing
    RemoveRune takes Runes d, integer RuneID returns nothing
    AddSpawn takes Runes d, rect r returns nothing
    RemoveSpawn takes Runes d, rect r returns nothing
    StartRuneTimer takes Runes d, real interval returns nothing
    ListRunes takes Runes d returns nothing
    ListSpawns takes Runes d returns nothing

JASS:
library RuneSystem initializer init

    globals
        // You can specify the max amount of Items/SpawnLocs each struct can have. 
        // The number shouldn't be too big, otherwise it will cause errors.
        private constant integer MAX_RUNES = 10
        
        // How often the periodic event fires.
        private constant real CHECK_INTERVAL = 0.05
    endglobals

                // END OF USER CONFIG!

    globals
        private item array Rune
        private hashtable Hash
    endglobals
                
    private interface RuneFace
        method onSpawn takes nothing returns nothing defaults nothing
        method onPickup takes nothing returns nothing defaults nothing
        method onPeriodic takes nothing returns nothing defaults nothing
        method onItemDestroy takes nothing returns nothing defaults nothing
    endinterface
                
    struct Runes extends RuneFace

        private integer RuneCount
        private integer SpawnCount
        
        private integer array Items[MAX_RUNES]
        private rect array SpawnLoc[MAX_RUNES]
        
        static unit TriggerUnit
        static item TriggerRune
        
        static method create takes nothing returns Runes
            local Runes this = Runes.allocate()
            
            set this.RuneCount = 0
            set this.SpawnCount = 0
            
            return this
        endmethod
        
        method add takes integer rune returns nothing
            static if DEBUG_MODE then
                if HaveSavedInteger(Hash, rune, this) then
                    debug call BJDebugMsg("ERROR: Adding an same item twice is not possible!")
                    return
                endif
            endif
            
            set this.Items[this.RuneCount] = rune
            set this.RuneCount = this.RuneCount + 1
            
            call SaveInteger(Hash, rune, this, this.RuneCount)
        endmethod
        
        method remove takes integer rune returns nothing
            local integer pos = LoadInteger(Hash, rune, this)
            local integer i = pos
            local boolean witem = false
            
            loop
                exitwhen i >= this.RuneCount
                
                static if DEBUG_MODE then
                    if LoadInteger(Hash, this.Items[i], this) == 0 then
                        debug call BJDebugMsg("ERROR: Cannot remove an item that hasn't been added!")
                        set witem = true
                        exitwhen witem
                    endif
                endif
                
                set this.Items[i] = this.Items[i+1]
                call SaveInteger(Hash, this.Items[i], this, i)
                
                set i = i + 1
            endloop
            
            if not witem then
                set this.RuneCount = this.RuneCount - 1
            endif
        endmethod
        
        method addSpawn takes rect r returns nothing
            static if DEBUG_MODE then
                if HaveSavedInteger(Hash, GetHandleId(r), this) then
                    call BJDebugMsg("ERROR: Cannot add the same rect twice.")
                    return
                endif
            endif
            
            set this.SpawnLoc[this.SpawnCount] = r
            set this.SpawnCount = this.SpawnCount + 1
            
            call SaveInteger(Hash, GetHandleId(r), this, this.SpawnCount)
        endmethod
        
        method removeSpawn takes rect r returns nothing
            local integer pos = LoadInteger(Hash, GetHandleId(r), this)
            local integer i = pos
            local boolean wrect = false
            
            loop
                exitwhen i >= pos
                
                static if DEBUG_MODE then
                    if LoadInteger(Hash, GetHandleId(this.SpawnLoc[i]), this) == 0 then
                        debug call BJDebugMsg("ERROR: Cannot remove an rect that hasn't been added!")
                        set wrect = true
                        exitwhen wrect
                    endif
                endif
                
                set this.SpawnLoc[i] = this.SpawnLoc[i+1]
                call SaveInteger(Hash, GetHandleId(this.SpawnLoc[i]), this, i)
                
                set i = i + 1
            endloop
            
            if not wrect then
                set this.SpawnCount = this.SpawnCount - 1
            endif
        endmethod
        
        method list takes nothing returns nothing
           local integer i = 0
           local item rune
           loop
               exitwhen i > this.RuneCount
               set rune = CreateItem(this.Items[i], 0, 0)
               call BJDebugMsg("Item Name in Pos " + I2S(LoadInteger(Hash, this.Items[i], this)) + ": " + GetItemName(rune))
               call RemoveItem(rune)
               set i = i + 1
           endloop
           set rune = null
        endmethod
        
        method spawnList takes nothing returns nothing
            local integer i = 0
            loop
                exitwhen i > this.SpawnCount
                call BJDebugMsg("X of Rect in Pos " + I2S(LoadInteger(Hash, GetHandleId(this.SpawnLoc[i]), this)) + ": " + I2S(R2I(GetRectCenterX(this.SpawnLoc[i]))))
                call BJDebugMsg("Y of Rect in Pos " + I2S(LoadInteger(Hash, GetHandleId(this.SpawnLoc[i]), this)) + ": " + I2S(R2I(GetRectCenterY(this.SpawnLoc[i]))))
                set i = i + 1
            endloop
        endmethod
        
        method startTimer takes real interval returns nothing
            local timer t = CreateTimer()
            local timer c = CreateTimer()
            call SaveInteger(Hash, GetHandleId(t), 0, this)
            call SaveInteger(Hash, GetHandleId(c), 0, this)
            
            call TimerStart(t, interval, true, function thistype.createRune)
            call TimerStart(c, CHECK_INTERVAL, true, function thistype.check)
            
            set t = null
            set c = null
        endmethod
        
        private static method createRune takes nothing returns nothing
            local Runes this = LoadInteger(Hash, GetHandleId(GetExpiredTimer()), 0)
            
            local integer i = GetRandomInt(0, this.SpawnCount-1)
            
            if Rune[this] == null then
                set Rune[this] = CreateItem(this.Items[GetRandomInt(0, this.RuneCount-1)], GetRectCenterX(this.SpawnLoc[i]), GetRectCenterY(this.SpawnLoc[i]))
                
                call SaveInteger(Hash, GetHandleId(Rune[this]), 0, this)
                
                set this.TriggerRune = Rune[this]
                call this.onSpawn()
            endif
        endmethod
        
        private static method check takes nothing returns nothing
            local Runes this = LoadInteger(Hash, GetHandleId(GetExpiredTimer()), 0)
            
            set this.TriggerRune = Rune[this]
            
            if Rune[this] != null then
                call this.onPeriodic()
            endif
            
            if GetWidgetLife(Rune[this]) < 0.405 and not (Rune[this] == null) then
                call this.onItemDestroy()
                
                set Rune[this] = null
            endif
        endmethod
        
    endstruct

    function InitRunes takes nothing returns Runes
        return Runes.create()
    endfunction

    function AddRune takes Runes d, integer rune returns nothing
        call d.add(rune)
    endfunction

    function RemoveRune takes Runes d, integer rune returns nothing
        call d.remove(rune)
    endfunction

    function AddSpawn takes Runes d, rect r returns nothing
        call d.addSpawn(r)
    endfunction

    function RemoveSpawn takes Runes d, rect r returns nothing
        call d.removeSpawn(r)
    endfunction

    function StartRuneTimer takes Runes d, real interval returns nothing
        call d.startTimer(interval)
    endfunction

    function ListRunes takes Runes d returns nothing
        call d.list()
    endfunction

    function ListSpawns takes Runes d returns nothing
        call d.spawnList()
    endfunction

    private function Pickup takes nothing returns boolean
        local item pRune = GetManipulatedItem()
        local Runes data = LoadInteger(Hash, GetHandleId(pRune), 0)
        
        if GetManipulatedItem() == Rune[data] then
            set data.TriggerUnit = GetTriggerUnit()
            set data.TriggerRune = pRune
            call data.onPickup()
            
            set Rune[data] = null
        endif
        
        return true
    endfunction

    private function init takes nothing returns nothing
        local trigger detectpickup = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(detectpickup, EVENT_PLAYER_UNIT_PICKUP_ITEM)
        call TriggerAddCondition(detectpickup, Condition(function Pickup))
        
        set Hash = InitHashtable()
        
        set detectpickup = null
    endfunction

endlibrary

Keywords:
rune, runes, rune system, system, aos, item, spawn, dota, item, items
Contents

Rune System (Map)

Reviews
20:51, 29th Aug 2009 hvo-busterkomo: The system could definitely be uselful although the coding is somewhat lacking in areas (what is with the module?).

Moderator

M

Moderator

20:51, 29th Aug 2009
hvo-busterkomo: The system could definitely be uselful although the coding is somewhat lacking in areas (what is with the module?).
 
Level 17
Joined
Sep 8, 2007
Messages
994
Why do you implement functions which actually don't do anything? That makes no sense for me xD
and why do you define the MAX_RUNES as textmacro? I guess there is a sensefully reason you did it like this ... but could you explain this to me?
I also don't really get the
JASS:
static key STRUCT
 
Level 11
Joined
Apr 29, 2007
Messages
826
If you want anything to happen, you can specify the functions to do something.
I tried defining it as a global, but JassHelper somehow said it would be a wrong size definition then. So I had to do it that way. :/
And they key is just for my own purpose. I use it instead of writing
JASS:
StringHash( "Struct" )
everytime. I could just insert 0 there, but it's more understandable this way.
 
Level 16
Joined
Oct 12, 2008
Messages
1,570
Looks like a nice system, though i got 2 little problems with it:
1) You never null any local handles.

2)
JASS:
method add takes integer rune returns nothing
    set .Runecount = .Runecount + 1
    if HaveSavedInteger(Hash, rune, this) then
        debug call BJDebugMsg("Error: Adding an same item twice is not possible!")
        return
    endif
    set .Items[.Runecount] = rune
    call SaveInteger(Hash, rune, this, .Runecount)
endmethod
What if the item is already in the struct? You return under the if. But you already increased .Runecount!!
Increase .Runecount under the endif, not above the if.
 
Level 17
Joined
Mar 17, 2009
Messages
1,349
Ok I'll test this later... but as an advice, don't use methods for system calls... I mean, alot of GUIers who might want to use this have no idea of what vJass is. And even for beginner Jassers, they won't know how structs work.

So instead of having Runes.create(), make a function CreateRune() which calls the method.

Ok, it might VERY SLIGHTLY decrease efficiency, but really, would it matter?
 
Level 16
Joined
Oct 12, 2008
Messages
1,570
Depending on the current JASSHELPERS inlining performance, the call should be inlined. And as the thing is in vJass, Jasshelper is needed to import the system. So it might even NOT decrease the efficiency. But i dont know how well Jasshelper inlines currently
 
Top