• 🏆 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!

[vJass] Need Feedback on System

Status
Not open for further replies.
Level 14
Joined
Nov 18, 2007
Messages
1,084
So I created a dummy recycling system, and I would like feedback on it before I actually try submitting it.
The main reason I'm not submitting this yet is because I'm not sure if this can be useful or if it's doing something more complicated than it should be.

Hopefully the documentation should be enough to explain what it does.
JASS:
//==========================================================================================
// DummyReuser by watermelon_1234
//******************************************************************************************
// A system designed to reuse/recycle dummy units of any type.
// This is mostly aimed for resusing dummy units that can be treated like special effects, 
// except that they can play a specific animation.
//******************************************************************************************
// Libraries required: (Libraries with * are optional)
//  - Table
//  - TimerUtils
//##########################################################################################
// API:
//  *reuser object*
//  Dummy units are reused by creating a reuser object. The best way to use this sytem is
//  have a global variable of type reuser.
//
//  *static method create takes integer id,boolean hasLocust returns thistype*
//  Create a reuser object with this method. id is the unit type id of the dummy unit you wnat
//  to recycle. hasLocust is used to determine if a dummy unit should have locust or not.
//  Once created, the struct has access to the following methods:
//
//  *method giveDummy takes player owner, real x, real y, real facing returns unit*
//  Gives a dummy unit by means of getting one in storage or by creating a new unit. 
//  Note that a dummy unit can not be reused with this method if DEFAULT_MAX_DUMMY_LIMIT
//  is reached.
//
//  *method reuseDummy takes unit dummy, real reuseDelay returns boolean*
//  Attempts to reuse a dummy unit if it can. Dummy units can be used in reuseDelay seconds.
//  If reuserDelay is equal or less than 0, the unit will be recycled instantly.
//  Returns false if the unit cannot be recycled.
//
//  *method canReuse takes unit dummy returns boolean*
//  Returns true if a dummy unit can be recycled by the struct.
//
//  *method destroy takes nothing returns nothing*
//  Use when you no longer need a reuser. Note that all dummy units in the struct's 
//  storage will get removed.  
//  
// Struct Members:
//  *boolean removeDummiesOnDestroy*
//  Set to false if you don't want dummy units to be removed when a reuser object is destroyed.
//
//  ~readonly integer unitID~
//  ~readonly boolean hasLocust~
//  ~readonly integer numberDummies~
//  These members should not be changed outside of the system's code. They're only intended
//  to be there so that you can check the value.  
//##########################################################################################
// Importing:
//  1. Copy this trigger.
//  2. Implement the required libraries.
//  3. Configure the system to your liking
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Notes:
//  - Dummy units should not be able to die.
//  - Having a higher DEFAULT_MAX_DUMMY_LIMIT will limit how many reuser objects you can have.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Credits:
//  - Vexorian for Table and TimerUtils. 
//==========================================================================================
library DummyReuser requires Table, TimerUtils

    globals
        // The maximum amount of dummies that would be stored by any reuser.
        private constant integer    DEFAULT_MAX_DUMMY_LIMIT = 15 
        // The player for the dummy  unit when it's in storage
        private constant player     DUMMY_STORAGE_OWNER = Player(15) 
        // Coordinates for the storage of the dummy
        private constant real       DUMMY_STORAGE_X = 0
        private constant real       DUMMY_STORAGE_Y = 0        
    endglobals
    
//==========================================================================================
// END OF CONFIGURATION
//==========================================================================================
    
    private keyword data  
    
    globals
        private constant integer ARRAY_LIMIT = DEFAULT_MAX_DUMMY_LIMIT-1 // Don't know why this won't work by using the value directly. 
    endglobals
       
    struct reuser
        readonly integer unitID // Unit id of the dummy unit you want reused
        readonly boolean hasLocust // Should the dummy units initially have locust or not?
        readonly integer numberDummies = 0 // The amount of dummies currently in storage   
        boolean removeDummiesOnDestroy = true // Whether or not to remove the dummy units when a reuser object gets destroyed.
        
        readonly unit array curDummies[ARRAY_LIMIT] // Stores all the dummy units currently used. 
        readonly boolean array inUse[ARRAY_LIMIT] // Determines whether or not a unit can be used at the moment.        
        
        private static HandleTable DummyID // To get the dummy's id assigned in the array
                       
        private method onDestroy takes nothing returns nothing
            local integer i = 0            
            loop
                exitwhen i >= .numberDummies
                if .removeDummiesOnDestroy then
                    call RemoveUnit(.curDummies[i])
                endif
                set .curDummies[i] = null
                set .inUse[i] = false
                set i = i + 1
            endloop
        endmethod    
        
        method canReuse takes unit dummy returns boolean
            return thistype.DummyID.exists(dummy)
        endmethod
        
        method giveDummy takes player owner, real x, real y, real facing returns unit
            local integer i = 0 
            // Search for any available dummy unit
            loop
                exitwhen i >= .numberDummies
                if .curDummies[i] != null and not .inUse[i] then
                    // Make sure the dummy unit is properly set up to be used.                    
                    call SetUnitOwner(.curDummies[i],owner,true)
                    call SetUnitFacing(.curDummies[i],facing)
                    call SetUnitX(.curDummies[i],x)
                    call SetUnitY(.curDummies[i],y)
                    call ShowUnit(.curDummies[i],true)
                    if .hasLocust and UnitRemoveAbility(.curDummies[i],'Aloc') then
                        call UnitAddAbility(.curDummies[i],'Aloc')
                    endif
                    set .inUse[i] = true
                    return .curDummies[i]
                endif
                set i = i + 1                
            endloop            

            // If the max limit has not been reached, create a dummy unit and allow it to be reused. 
            // Else, just do a CreateUnit call
            if .numberDummies < DEFAULT_MAX_DUMMY_LIMIT then
                set .curDummies[.numberDummies] = CreateUnit(owner,.unitID,x,y,facing)
                set thistype.DummyID[.curDummies[.numberDummies]] = .numberDummies
                set .inUse[.numberDummies] = true
                set .numberDummies = .numberDummies + 1                
                return .curDummies[.numberDummies-1]
            else
                debug call BJDebugMsg(SCOPE_PREFIX + ": Exceeded dummy limit, new unit was created.") 
                return CreateUnit(owner,.unitID,x,y,facing)
            endif            
        endmethod      
       
        method reuseDummy takes unit dummy, real reuseDelay returns boolean
            if .canReuse(dummy) then
                if .inUse[thistype.DummyID[dummy]] then
                    if reuseDelay > 0 then
                        call data.create(this,thistype.DummyID[dummy],reuseDelay)
                    else
                        call ShowUnit(.curDummies[thistype.DummyID[dummy]],false)
                        call SetUnitOwner(.curDummies[thistype.DummyID[dummy]],DUMMY_STORAGE_OWNER,false)
                        call SetUnitX(.curDummies[thistype.DummyID[dummy]],DUMMY_STORAGE_X)
                        call SetUnitY(.curDummies[thistype.DummyID[dummy]],DUMMY_STORAGE_Y)
                        
                        set .inUse[thistype.DummyID[dummy]] = false 
                    endif                    
                    return true
                debug else
                    debug call BJDebugMsg(SCOPE_PREFIX + ": Dummy unit is not in use.")
                endif                
            debug else
                debug call BJDebugMsg(SCOPE_PREFIX + ": An invalid unit given to be reused.")
            endif
            return false
        endmethod       
        
        static method create takes integer id,boolean hasLocust returns thistype
            local thistype this = thistype.allocate()
            set .unitID = id
            set .hasLocust = hasLocust     
            return this
        endmethod
        
        private static method onInit takes nothing returns nothing
            set thistype.DummyID = HandleTable.create()
        endmethod
    endstruct
    
    // Struct used to reuse dummy units later
    private struct data
        reuser r
        integer id
        timer tim
        
        static method onExpire takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())            
            call ShowUnit(.r.curDummies[.id],false)
            call SetUnitOwner(.r.curDummies[.id],DUMMY_STORAGE_OWNER,false)
            call SetUnitX(.r.curDummies[.id],DUMMY_STORAGE_X)
            call SetUnitY(.r.curDummies[.id],DUMMY_STORAGE_Y)            
            set .r.inUse[.id] = false 
            
            call ReleaseTimer(.tim)
            call .destroy()
        endmethod
        
        static method create takes reuser r, integer id, real delay returns thistype
            local thistype this = thistype.allocate()
            set .r = r
            set .id = id
            set .tim = NewTimer()
            call SetTimerData(.tim,this)
            call TimerStart(.tim,delay,false,function thistype.onExpire)            
            return this
        endmethod
    endstruct
endlibrary
I have also attached a test map which has a simple spell using the system.

I hope I'm not offending baassee with this because he already submitted his own system. :O I had worked on this system before I saw his.

Credits:

  • Vexorian for libraries used in test-map
  • aznricepuff for xeprojectile
 

Attachments

  • DummyReuserTest.w3x
    51 KB · Views: 63
Last edited:
Level 14
Joined
Nov 18, 2007
Messages
1,084
Thanks. :D

If a struct gets destroyed, I thought of it as the user not wanting to reuse/recycle that specific unit id ever in the map.
Primarily, a reuser object should only be made once (as a global) for every different dummy id there is.
It actually should never get destroyed. Just in case someone did destroy the struct, I decided to remove the dummy units since they probably couldn't be accessed anymore.

I'm not sure if I made a lot of sense.
 
watermelon_1234 said:
If a struct gets destroyed, I thought of it as the user not wanting to reuse/recycle that specific unit id ever in the map.
Primarily, a reuser object should only be made once (as a global) for every different dummy id there is.
It actually should never get destroyed. Just in case someone did destroy the struct, I decided to remove the dummy units since they probably couldn't be accessed anymore.

You may want to create a constant which would let the user pick if the dummies get destroyed or not, and place RemoveUnit inside a static if. Either that or have public struct member which would determine if the unit should get removed or not, for example;

JASS:
set Struct.removeDummies = false
call Struct.destroy()

And have it default to true.
 
Status
Not open for further replies.
Top