HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Techno's Timer System v.Horrible

11-29-2007, 03:36 AM#1
O_O Technomancer
TGS (Techno's horrible/unfinished Gunattach System) is also attached.

Updated Nov 30 '07:
Many pointless, inlinable functions removed
Fiddled with particle engine a little
Commented the hell out of everything.

Concept:
To be able to add units to a timer easily and effeciently so that a single timer can trigger more than one event. This is particularly useful to smooth motion systems that rely on having a short timer but do not want to allocate a ton of processor time to multiple short timers (basically anything more than 3 short timers gets pretty ridiculous).

Realization:
This system does not realize the concept of "easily" or "effeciently", is horribly and possibly incorrectly commented, and is being posted for review. This is my first submission, I'm relatively new to the world editor but I have read alot of Cohadar's angry posts so those are the things I'm working on at the moment.


Pros:
It works fine as far as I can tell, and is pretty much bug-free.
Uses ABC which is nice an easy to understand.

Cons:
The code is awful
At present it requires the use of the inherent badly coded structures
I use sentences for variable names except for some instances where I get retarded with it and do whatever.
Probably leaky as hell.

Demo map and code coming shortly, edits ftw.

Collapse JASS:
// ------------------ VERSIONS AND CHANGELOG ----------------------------------
// Version: .03 BETA                                                       
// Changelog:
// .03 beta - fully commented with test map
// .02 beta - many errors fixed, some comments added, unreleased
// .01 beta - initial public release, poorly coded and commented, some errors
// 
// ------------------- IMPROVEMENTS FORTHCOMING -------------------------------
// Need to figure out exactly how to null some vars and clean up some leaks
// Need to figure out how to null function interface pointers.
// Optimization!!
// More optimizations!
// Reducing Cohadar Attack Index below 30%
//
// ------------------- GET ON WITH IT ALREADY ---------------------------------


// These functions and structs are the core of the Techno Timer System Library
// 
// DOCUMENTATION
// I'm generally bad at documentation so please, if you would like anything explained,
// please, PM me @Technomancer on [url]www.thehelper.net[/url], and I'll add it to the documentation
// 
// ***************************************************************************************
// The general idea is to run your map according to a global timer, instead of creating
// and destroying any number of individual timers, because as a rule we try to cut down
// on trigger events and function calls during the execution of a map, and CreateTimer(),
// DestroyTimer(), and espeically TimerStart(...) and the associated triggers will lag your
// shit up, that's a gaurentee.  Several maps already incorporate global event timers into
// themselves, this is just a premade package that will allow you to easily do so in your 
// map.
// 
// ***************************************************************************************
// This trigger set uses vJass.  The only other effective system for doing this sort of 
// would be to use gamecache based handle var systems, which in rare cases are buggy,
// but can also slow the game excessively after a large amount of data is transferred.
//
// ***************************************************************************************
//
// This is how this pacakage will work with new timer operated units:
// Step 1) Create a Unit, and a non-private, non-method Function that follows the syntax
//            of function YOUR_NAME HERE takes unit interface_unit returns nothing
//            Keep it exactly the same, except for the name.
// Step 2) Create a timer structure (Techno_TimerStruct)
// Step 3) Assign a struct of type Techno_GunUnitAttach to the unit's custom value.
//            *No custom libraries are required for this, just use 
//                    call SetUnitUserData( UNIT , STRUCT_NAME )
// Step 4) Assign the unit and an associated function to the timer
//               the timer can now automatically pick out the unit's necessary 
//               custom values from the struct attached to the unit, and
//               will automatically send the unit with those values to the
//               function which you selected for it, at the interval you select
//
//        '''''''''''''''''''''''''''''''''''''''''''''''''''''''''
//      EXAMPLE
//        call YOUR_TIMER_STRUCT_NAME.Timer_Add_Unit( YOUR_UNIT_NAME, UNIT'S_FUNCTION_NAME )
//        Obviously, replace the stuff in all caps with your names
//        '''''''''''''''''''''''''''''''''''''''''''''''''''''''''               
// Step 5) Activate the timer if necessary.  The timer will automatically pass the unit
//               to the required function.
// Step 6) Any additional data you wish to pass can be passed in the structure you
//         assigned in the Unit's custom value.  This allows total modularity, because
//         any function that can access the unit can access all of the values that are
//         involved in the function
//                
//         I would love some help textmacroing this up to be more modular, so that the unit
//         can take differently named structs, and the function interface can take different
//         arguements if the user so desires.



//TTS: Techno Timer System
library TTS uses ABC

//Declare globals and stuff
globals

    constant integer MAX_UNITS = 64                 // max array size
    constant integer TESTER_U_ID = 'h000'           //ID of tester unit
                                                    // necessary?
    
endglobals

// ** ALL function interface OBJECTS OF TYPE "INTERFACE" MUST USE THIS SYNTAX **
// ** COPY AND PASTE: DO NOT CHANGE THE WORDS, OR ADD ANYTHING                **
// **        you are welcome to edit it should you see fit.                   **
// **     You can pass whatever you need through the unit using the struct    **
// **        attached to it                                                   **
function interface INTERFACE takes unit interface_unit returns nothing
// **       This is NOT A FUNCTION
//  **       This is NOT A FUNCTION
//   **       This is NOT A FUNCTION , It is a function interface
//  **       This is NOT A FUNCTION       used for getting pointers to functions
// **       This is NOT A FUNCTION



//This function is called whenever the timer hits 0.
//It is generic and (UNTESTED) should work with any number of created timers.
//This function does not do anything but loop, get the required functions and
//their associated units, and then pass the units to the functions, while the
//functions execute.

function Timer_Action_Functions takes nothing returns nothing
    local integer i = 0
    local unit u
    local Techno_GunUnitAttach gAttach = Techno_GunUnitAttach.create()
    local Techno_TimerStruct thisTimer
    local INTERFACE f
    set thisTimer = GetTimerStructC(GetExpiredTimer())
    
    
    loop
        exitwhen i >= thisTimer.Num_Units
            //Get Function Attached GunStruct
            set u = thisTimer.Unit_Array[i]
            set gAttach = GetUnitUserData(u)
            set gAttach.timercounter = gAttach.timercounter + 1
            //call BJDebugMsg("timercounter is set to: " + I2S(gAttach.timercounter))
            if (gAttach.timercounter >= gAttach.max_iterations) then
                call BJDebugMsg("    gAttach.timercounter = " + I2S(gAttach.timercounter) + "  |  gAttach.max_iterations = " + I2S(gAttach.max_iterations) )
                call SetUnitUserData(u, 0) //Removes the custom value from the unit
                call RemoveUnit(u) // Kill the unit when the timer expires
                call thisTimer.Timer_Remove_Unit_Enum(i)
                
                //gattach is reused so do not destroy it!!!
    // *** There is a leak here, but I'm unsure how to fix it. ***
    
    
    // *** NEED TO ADD CODE FOR PERMENANT PERIODIC TIMERS *** //
    // *** Probably will just add an if check to see if 
                
                //Notice that in thise side of the if statement the counter is not increased!
                //This is to prevent units being skipped when they replace a removed unit
                //in the Timer_Remove_Unit_Enum function
                
                //Unfortunately the faster version of that function is not presently
                //Being implemented.
                
                
                
                
            else // Specific unit timer is not expired
            //Get associated function of gunstruct
                set f = thisTimer.Unit_Funcs_Array[i]
                call f.execute(u)
                set i = i + 1
            endif
        endloop
    set u = null
endfunction

struct Techno_TimerStruct
    timer T_Timer = CreateTimer()
    //In future versions, both arrays may be changed to linked lists of units.
    unit array Unit_Array[MAX_UNITS]
    INTERFACE array Unit_Funcs_Array[MAX_UNITS]
    real cooldown = .01
    integer Num_Units = 0
    boolean TimerActive = false

    method TimerAddUnit takes unit u, INTERFACE f returns nothing
        //First we check to make sure we have enough space
        if this.Num_Units < MAX_UNITS then
        //Then we set the unit equal to the appropriate array,
        //add the appropriate function to the corresponding array
        //and increase the counter for the number of units by 1
            set this.Unit_Array[this.Num_Units] = u
            set this.Unit_Funcs_Array[this.Num_Units] = f
            set this.Num_Units = this.Num_Units + 1
        else
            //if too many units:
            call BJDebugMsg("Error: Unit Cap Reached on Timer | From TTS")
        endif
        set u = null
    endmethod
    

    
//method TimerAddUnitLinkedList coming soon!
    
    
    //EASILY INLINABLE: DO NOT CALL THIS FUNCTION
    method Timer_Get_Last_Unit takes nothing returns unit
        local unit u = this.Unit_Array[this.Num_Units]
        return u
    endmethod
    
    //Removes the last unit from the list.  Inlinable but prettier
    // if not inlined.
    method Timer_Remove_Last_Unit takes nothing returns nothing
        set this.Unit_Array[this.Num_Units] = null
        set this.Num_Units = this.Num_Units - 1
        if this.Num_Units < 0 then
            set this.Num_Units = 0
        endif
    endmethod


    // This function needs to be more thoroughly tested for buggers and leaks
    method Timer_Remove_Unit_Enum takes integer unum returns nothing
        //This one could use rescripting... linked lists mmmm
        //Replace each unit with the one in front of it, starting with the unum unit
        //Do the same for the unit functions
        if this.Num_Units == unum + 1 then
            //CODE HERE IS INLINED FROM Timer_Remove_Last_Unit
            //Just removes the last unit from the array
            
            call BJDebugMsg("First Remove method | num_units = " + I2S(this.Num_Units) + " | enum = " + I2S(unum) )
            set this.Unit_Array[this.Num_Units] = null
            set this.Num_Units = this.Num_Units - 1
        else
        
            // Credit to Cohadar of WC3Campaigns.net, I stole this idea from ABC:
            set this.Unit_Array[unum] = this.Unit_Array[this.Num_Units - 1]
            set this.Unit_Array[this.Num_Units - 1] = null
            set this.Unit_Funcs_Array[unum] = this.Unit_Funcs_Array[this.Num_Units - 1]
            //set this.Unit_Array[this.Num_Units] = null

            //Not implemented due to problem interfacing with Timer_Action_Functions
            
            //OLDWAY: if during testing there are no unfixable problems, this code
            // will be removed.
            //loop
            //    exitwhen unum > this.Num_Units
            //    set this.Unit_Array[unum] = this.Unit_Array[unum + 1]
            //    set this.Unit_Funcs_Array[unum] = this.Unit_Funcs_Array[unum + 1]
            //    set unum = unum + 1
            //endloop
            //set this.Unit_Array[this.Num_Units] = null
            //ENDOLDWAY
            
//            set this.Unit_Funcs_Array[this.Num_Units] = null
//      *** FOR THIS LINE, I NEED TO LOOK UP THE SYNTAX TO REMOVE A FUNCTION 
//          POINTER... null apparently is incorrect?                     ***
//          For right now, it will be re-initialized later (should be)
//          So this isn't much of a problem and should only cause serious issues
//          in systems that initialize many, many timers and don't add alot of
//          units to each one.
           set this.Num_Units = this.Num_Units - 1
        endif
    endmethod
    
//Timer Start Function:
    method Timer_TimerStart takes nothing returns nothing
        set this.TimerActive = true
            //to prevent duplicate activation which causes errors
        
        call SetTimerStructC(this.T_Timer, this)
            //so the timer can access it's own info
        
        //start the timer
        call TimerStart(this.T_Timer, this.cooldown, true, function Timer_Action_Functions)

    endmethod
    
endstruct

endlibrary

The techno_gununitthinger that is referenced a couple of times above: (incomplete but posted for references)

Collapse JASS:
// ------------------ VERSIONS AND CHANGELOG ----------------------------------
// Version: .03 BETA                                                       
// Changelog:
// .03 beta - fully commented with test map
// .02 beta - many errors fixed, some comments added, unreleased
// .01 beta - initial public release, poorly coded and commented, some errors
// 
// ------------------- IMPROVEMENTS FORTHCOMING -------------------------------
// Need to figure out exactly how to null some vars and clean up some leaks
// Need to figure out how to null function interface pointers.
// Optimization!!
// More optimizations!
// Reducing Cohadar Attack Index below 30%
//
// ------------------- GET ON WITH IT ALREADY ---------------------------------

// *********************************************************************** //
// This is the techno gun system library.  I made it so I could             |
// shoot shit across the map using my global timer, mostly frogs.           |
// This library uses ABC Struct Attachment System, available at             |  
// [url]www.WC3Campaigns.net[/url], and the World Editor Section of                    |
// [url]www.thehelper.net[/url], and invented by Cohadar of [url]www.wc3campagins.net[/url]       |
//                                                                          | 
// Both sites are great mapping resources.  Anyway, on with the FAQ:        |
//                                                                          |
// KNOWN ISSUES:                                                            |
// IF YOU ATTACH THE SAME STRUCT TO MULTIPLE UNITS, THEN ALL UNITS          |
// WILL USE THE EXACT SAME DATA TO MOVE, AND IF YOU CHANGE THE DATA IT WILL |
// CHANGE FOR ALL UNITS.  This is not a bug, this is just you being lazy.   |
// If you want multiple units w/ different values then use different        |
// structs.  If you want multiple units on the same struct, then just use   |
// one, which is actually a cool, but unintentional feature.                |
//                                                                          |
// *********************************************************************** //

// ************************* LIBRARY CONTAINS WHAT? ********************** //
// This library contains a struct to attach to your units, and a bunch of ->| (incomplete lol)
// physics based functions which you absolutely don't have to use.          |
// You don't have to actually use either of them to use the timer system,   |
// Just change the name of the struct used from Techno_GunUnitAttach to     |
// Your_Struct_Name.  Only struct attachments are supported, get your       |
// handle vars out of here!                                                 |
//                                                                          |
// If you do change that sort of thing, you can just disable this library   |
// and use it for reference.                                                |
//                                                                          |
// Eventually the timer system will use TextMacros to allow you to          |
// determine what kind of struct you want to use, and in fact, I could      |
// probably do it right now if I really wanted to, but that's on the to-do  |
// list.                                                                    |
// ************************************************************************//

// Please note: If you copy this library into your system from another map,
// please take the time to download the newest version from [url]www.wc3campaigns.net[/url]
// or [url]www.thehelper.net[/url].  It will run faster and be less buggy, and it will
// save me, Cohadar, and everyone else at those forums a whole lot of trouble
// with noobs reposting old issues.



library TGS requires ABC
struct Techno_GunUnitAttach
    integer timercounter //counts timer iterations
    integer max_iterations
    integer projectile_unit
    real jump_dist
    //targetting declares
    unit targettingunit
    real fixedx
    real fixedy
    real angle
    
    method SetProjectileType takes integer UnitType returns nothing
        set this.projectile_unit = UnitType
    endmethod

    method GetProjectileType takes nothing returns integer
        return this.projectile_unit
    endmethod
    
    method GetTimerCounter takes nothing returns integer
        return this.timercounter
    endmethod
    
    method CalcTargAngle_UnitBased takes real sourceX, real sourceY returns real
        return (bj_RADTODEG * Atan2( (GetUnitY(this.targettingunit) - sourceY ), (GetUnitX(this.targettingunit) - sourceX) ))
    endmethod
    
    method CalcTargAngle_targ2 takes real sourceX, real sourceY returns real
        return (bj_RADTODEG * Atan2( (this.fixedy - sourceY ) , ( this.fixedx - sourceX ) ) )
    endmethod

    method Init takes integer iterations, real jumpdist, real angle returns nothing
        set this.timercounter = 0
        set this.projectile_unit = 'nfro' //make sure to change this if your shit is coming out in frogs, dumbass.
        set this.fixedx = 0.
        set this.fixedy = 0.
        set this.angle = angle
        set this.jump_dist = jumpdist
        set this.max_iterations = iterations
    endmethod
    
    method onDestroy takes nothing returns nothing
        set this.targettingunit = null
    endmethod
endstruct


// FOR QUiCK REFERENCE // Techno_GunAttachUnit MEMBERS //
//    integer timercounter //counts timer iterations
//    integer max_iterations
//    integer projectile_unit
    //targetting declares
//    integer targetting_function_type = 2
//    unit targettingunit
//    real fixedx
//    real fixedy
//    real angle
//                     //                              //


        // FOR TGSLinearMove
        // Variables necessary from tgs
        // fixedx
        // fixedy
    function TGSLinearMove takes unit interface_unit returns nothing
        //Move unit in a straight TOWARDS A POINT line according to distance every tick
        //In general, it is better to set the angle rather than recalculate the angle every time
        //See function TGSMoveByAngle
        local Techno_GunUnitAttach tgs = GetUnitUserData(interface_unit)
        local real x = GetUnitX(interface_unit)
        local real y = GetUnitY(interface_unit)
        local real angle = bj_RADTODEG * Atan2( x - tgs.fixedx,  y - tgs.fixedy)

        call BJDebugMsg(R2S(angle) + "is your angle sir")
        
        set x = x + tgs.jump_dist * Cos(angle * bj_DEGTORAD)
        set y = y + tgs.jump_dist * Sin(angle * bj_DEGTORAD)
        
        call SetUnitPosition(interface_unit, x, y)
        
        set interface_unit = null
    endfunction
    
    function TGSMoveByAngle takes unit interface_unit returns nothing
        //Move unit and adjust move angle each time.
        local Techno_GunUnitAttach tgs
        local real x
        local real y

        set tgs = GetUnitUserData(interface_unit)

        //From the PolarProjectionBJ function, uses triangles n stuff to calcluate vectors n stuff
        set x = GetUnitX(interface_unit) + tgs.jump_dist * Cos(tgs.angle * bj_DEGTORAD)
        set y = GetUnitY(interface_unit) + tgs.jump_dist * Sin(tgs.angle * bj_DEGTORAD)
        call SetUnitPosition(interface_unit, x, y)
        
        set interface_unit = null
    endfunction
    
    function TGSArcMoveByAngle takes unit interface_unit returns nothing
        
    endfunction
    
    function TGSArcMoveByDxDy takes unit interface_unit returns nothing
        //Move unit in an arc pattern, and use distance modifiers instead of angle modifiers
    endfunction
    
    function TGSMathFuncMove takes unit interface_unit returns nothing
        //Move unit following a mathematical function.
    endfunction
    
    function TGSLinearMove_AdjustWithMathFunc takes unit interface_unit returns nothing
        //Moves unit following the path of a straight line, an d adjusts the pathing of the unit using a mathematical function
    endfunction

    function TGSCollision_Detect takes nothing returns nothing
        local trigger trig = GetTriggeringTrigger()
        //Get applicable units
        //Get EFFECTS functions
        //Apply EFFECTS to target
        //Clean Up Trigger
        //Remove Triggering Unit from the Game
    endfunction
endlibrary



Major issues:
With heavy usage I run out of structs because I'm not catching leaks. Problem is, I don't really know how to clean up leaks with structs? Nulling them results in an error, and I was under the impression that they would auto-remove at the end of a function.
This also causes minor system lag after about 40 frogs are airborne in the test map, on my crappy e-machine. Updates now include more randomness, and alot more test casters. Eventually I'll make the test map user friendly, even.
Attached Files
File type: w3xTTS.w3x (51.9 KB)
12-01-2007, 01:09 PM#2
Vexorian
You are using a library, exploit it.

A lot of your names should be private or public, specially that horrid INTERFACE one that just got such a generic name... When you have things like "DO NOT CALL THIS" in your code it looks like you want to use private ...

Quote:
With heavy usage I run out of structs because I'm not catching leaks. Problem is, I don't really know how to clean up leaks with structs? Nulling them results in an error, and I was under the impression that they would auto-remove at the end of a function.
You are supposed to destroy them...

Methods full of prefixes, this doesn't make a lot of sense.

I read the code and your explanation a couple of times and I still am not sure what this system does.
12-02-2007, 12:01 AM#3
moyack
If I understood right, this library is intended to control with one timer several units with variable events, and this library is for being used by developers of physics system. WOAHHH :P

Man, if any developer commit the mistake to do a physic system which uses several timers to manage independent situations, then this dude is approaching to the mathematic modeling in the worst way possible, and doing a library for that won't clarify or help in any way.

My suggestions to develop a library:
  • Determine if you have a hard and repetitive problem that needs to be solved.
  • Define what do you REALLY need to solve it. Many many people start with a simple parabolic movement emulation and finish with a uber nukem system which manages over complicated stuff, this usually will be inefficient or ends bad coded, hard to understand and, as a consequence, useless.
  • Analyze your alternatives to solve the problem with the tools you currently have. I mention this because many problems can be solved not only with code, probably using something already done by Blizzard (not all the features of WE are in the Trigger Editor :P).
  • If the problem definitely requires a code to solve it, then develop the code.
  • In the developing process, try to make it as easier to understand as possible, keeping a high level of coherency.
  • After finishing your library, if you consider it can be useful to the community, then submit it, so many others can simplify their coding with your proposal.

I hope this help you in your learning process. Because you're more interested in suggestions and comments rather than submit this code, then I'll move it to the proper section.