• 🏆 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!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

[Crash] My Map Doesn't Work For No Reason?

Status
Not open for further replies.
Level 1
Joined
Dec 21, 2016
Messages
7
Hello everyone!

I have got a Hero Arena Like Map, and recently I tried to make AI for it. I download AI Examples and put it in my map, and when I save it there are no errors or whatever.

But when I try to launch it in WC3 this map is doesn't work...

What's wrong with it?

I attached the whole map, so anyone can test what's wrong with it...

I have an idea, that the map doesn't work, because I use OLD (maybe) Cache System (You can find it in the custom script in the map. If so, how to up-date?

Also any help and advices are appreciated.

Thanks!
 

Attachments

  • NEWTEST.w3x
    248.8 KB · Views: 61
Last edited:
Level 13
Joined
Mar 24, 2013
Messages
1,105
Yeah, pretty much, GUI never was able to capitalize on the return bug without custom script usage. You're looking for places like tutorial shows, where it has the two returns and its not a conditional.
 
Level 1
Joined
Dec 21, 2016
Messages
7
Yeah, pretty much, GUI never was able to capitalize on the return bug without custom script usage. You're looking for places like tutorial shows, where it has the two returns and its not a conditional.
I tried all the guides, that I find, and it is too complicated or described quite badly.... I think I should ask someone to help me for some "reward".

If you can help me, I can give you something back as a Christmas gift... Any Steam Game for example... Thank you...
 
Level 1
Joined
Dec 21, 2016
Messages
7
Post your entire Custom Script section here between [code=jass][/code] tags and I can show you which lines to change.
JASS:
//********************************************************************************************************
//*                                                                                                      *
//* Caster System 12.8 Cache Module (http://vexorian.wc3campaigns.com/ )                                 *
//* _______________________________                                                                      *
//* CSCache module, a part of Vexorian's Caster System which you can find at the above URL.              *
//*                                                                                                      *
//********************************************************************************************************

//##Begin of CS Gamecache engine##
//=================================================================================================
// GameCache - Return bug module : Without gamecache or return bug, JASS would be a
// retarded-limited scripting language.
//
//=================================================================================================
// a.k.a H2I, changed name to CS_H2I to prevent conflicts with other systems (I intended this
// system to be easy to copy
//
function CS_H2I takes handle h returns integer
    return h
    return 0
endfunction
//=================================================================================================
// Main Gamecache handler
//
function CSCache takes nothing returns gamecache
    if udg_cscache==null then
        call FlushGameCache(InitGameCache("CasterSystem.vx"))
        set udg_cscache=InitGameCache("CasterSystem.vx")
        call StoreInteger(udg_cscache,"misc","TableMaxReleasedIndex",100)
    endif
 return udg_cscache
endfunction

//==================================================================================================
// Attachable vars : Attacheable variables are what most other people call Handle Variables, they
// allow to relate data with any handle, using a label, and its value, the stuff auto flushes if
// the value is 0, false, "", or null .
//
// Differences between Attacheable variables and "Local Handle Variables" :
// - The names of the functions
// - The name of the function group does not cause confusion, it is difficult to say: 
//   "you should set local handle variables to null at the end of a function" since
//   it sounds as if you were talking about the "Local Handle Variables"
// - Also Have Attacheable Sets.
// - And can work together with Tables.
// 
// Notes: don't "attach" variables on texttags nor those handle types used mostly for parameters
// (for example damagetype) , Although there is no reason to do so anyways
//
// Gamecache stuff are NOT Case Sensitive, don't ever use "" for label (Crashes game!)
//

//============================================================================================================
// For integers
//
function AttachInt takes handle h, string label, integer x returns nothing
 local string k=I2S(CS_H2I(h))
    if x==0 then
        call FlushStoredInteger(CSCache(),k,label)
    else
        call StoreInteger(CSCache(),k,label,x)
    endif
endfunction
function GetAttachedInt_FromSet takes handle h, gamecache g returns integer
    return GetStoredInteger(g,I2S(CS_H2I(h))+";"+GetStoredString(g,"argpass","set"),GetStoredString(g,"argpass","seti"))
endfunction
function GetAttachedInt takes handle h, string label returns integer
    if (label=="") then
        return GetAttachedInt_FromSet(h,CSCache())
    endif
 return GetStoredInteger(CSCache(), I2S(CS_H2I(h)), label)
endfunction

//=============================================================================================================
function AttachReal takes handle h, string label, real x returns nothing
 local string k=I2S(CS_H2I(h))
    if x==0 then
        call FlushStoredReal(CSCache(),k,label)
    else
        call StoreReal(CSCache(),k,label,x)
    endif
endfunction
function GetAttachedReal takes handle h, string label returns real
    return GetStoredReal(CSCache(),I2S(CS_H2I(h)),label)
endfunction

//=============================================================================================================
function AttachBoolean takes handle h, string label, boolean x returns nothing
 local string k=I2S(CS_H2I(h))
    if not x then
        call FlushStoredBoolean(CSCache(),k,label)
    else
        call StoreBoolean(CSCache(),k,label,x)
    endif
endfunction
function GetAttachedBoolean takes handle h, string label returns boolean
    return GetStoredBoolean(CSCache(),I2S(CS_H2I(h)),label)
endfunction

//=============================================================================================================
function AttachString takes handle h, string label, string x returns nothing
 local string k=I2S(CS_H2I(h))
    if x=="" then
        call FlushStoredString(CSCache(),k,label)
    else
        call StoreString(CSCache(),k,label,x)
    endif
endfunction
function GetAttachedString takes handle h, string label returns string
    return GetStoredString(CSCache(),I2S(CS_H2I(h)),label)
endfunction

//====================================a=========================================================================
function AttachObject takes handle h, string label, handle x returns nothing
 local string k=I2S(CS_H2I(h))
    if (x==null) then
        call FlushStoredInteger(CSCache(),k,label)
    else
        call StoreInteger(CSCache(),k,label,CS_H2I(x))
    endif
endfunction
function GetAttachedObject takes handle h, string label returns handle
    return GetAttachedInt(h,label)
    return null
endfunction
function GetAttachedWidget takes handle h, string label returns widget
    return GetAttachedInt(h,label)
    return null
endfunction
function GetAttachedRect takes handle h, string label returns rect
    return GetAttachedInt(h,label)
    return null
endfunction
function GetAttachedRegion takes handle h, string label returns region
    return GetAttachedInt(h,label)
    return null
endfunction
function GetAttachedTimerDialog takes handle h, string label returns timerdialog
    return GetAttachedInt(h,label)
    return null
endfunction
function GetAttachedUnit takes handle h, string label returns unit
    return GetAttachedInt(h,label)
    return null
endfunction
function GetAttachedItem takes handle h, string label returns item
    return GetAttachedInt(h,label)
    return null
endfunction
function GetAttachedEffect takes handle h, string label returns effect
    return GetAttachedInt(h,label)
    return null
endfunction
function GetAttachedDestructable takes handle h, string label returns destructable
    return GetAttachedInt(h,label)
    return null
endfunction
function GetAttachedTrigger takes handle h, string label returns trigger
    return GetAttachedInt(h,label)
    return null
endfunction
function GetAttachedTimer takes handle h, string label returns timer
    return GetAttachedInt(h,label)
    return null
endfunction
function GetAttachedGroup takes handle h, string label returns group
    return GetAttachedInt(h,label)
    return null
endfunction
function GetAttachedTriggerAction takes handle h, string label returns triggeraction
    return GetAttachedInt(h,label)
    return null
endfunction
function GetAttachedLightning takes handle h, string label returns lightning
    return GetAttachedInt(h,label)
    return null
endfunction
function GetAttachedImage takes handle h, string label returns image
    return GetAttachedInt(h,label)
    return null
endfunction
function GetAttachedUbersplat takes handle h, string label returns ubersplat
    return GetAttachedInt(h,label)
    return null
endfunction
function GetAttachedSound takes handle h, string label returns sound
    return GetAttachedInt(h,label)
    return null
endfunction


//============================================================================================================
// Attached Sets: Attachable Sets are handy in some situations and are a part of attachable variables,
// you can add integers or objects to a set, order doesn't matter and adding the same object twice is
// meaningless. CleanAttachedVars is always ready to clean every set owned by the handle.
//
//============================================================================================================
function AttachedSetAddInt takes handle h, string setn, integer int returns nothing
 local gamecache g=CSCache()
 local string k=I2S(CS_H2I(h))
 local integer n
 local integer x=GetStoredInteger(g,k,"#setnumberof;"+setn)
 local integer y
    if x==0 then
        set y=GetStoredInteger(g,k,"#totalsets")+1
        call StoreInteger(g,k,"#totalsets",y)
        call StoreInteger(g,k,"#setnumberof;"+setn,y)
        call StoreString(g,k,"#setName;"+I2S(y),setn)
    endif
    set k=k+";"+setn
    if not HaveStoredInteger(g,k,"Pos"+I2S(int)) then
        set n=GetStoredInteger(g,k,"n")+1
        call StoreInteger(g,k,"n",n)
        call StoreInteger(g,k,I2S(n),int)
        call StoreInteger(g,k,"Pos"+I2S(int),n)
    endif
 set g=null
endfunction
function AttachedSetAddObject takes handle h, string setn, handle val returns nothing
    call AttachedSetAddInt(h,setn,CS_H2I(val))
endfunction

//============================================================================================================
function AttachedSetHasInt takes handle h, string setn, integer int returns boolean
    return HaveStoredInteger(CSCache(),I2S(CS_H2I(h))+";"+setn,"Pos"+I2S(int))
endfunction
function AttachedSetHasObject takes handle h, string setn, handle val returns boolean
    return AttachedSetHasInt(h,setn,CS_H2I(val))
endfunction

//============================================================================================================
function GetAttachedSetSize takes handle h, string setn returns integer
    return GetStoredInteger(CSCache(),I2S(CS_H2I(h))+";"+setn,"n")
endfunction

//============================================================================================================
function AttachedSetRemInt takes handle h, string setn, integer int returns nothing
 local gamecache g=CSCache()
 local string k=I2S(CS_H2I(h))+";"+setn
 local integer n
 local integer x
 local integer y
    if HaveStoredInteger(g,k,"Pos"+I2S(int)) then
        set x=GetStoredInteger(g,k,"Pos"+I2S(int))
        set n=GetStoredInteger(g,k,"n")
        if x!=n then
            set y=GetStoredInteger(g,k,I2S(n))
            call StoreInteger(g,k,I2S(x),y)
            call StoreInteger(g,k,"Pos"+I2S(y),x)
        endif        
        call FlushStoredInteger(g,k,"Pos"+I2S(int))
        call FlushStoredInteger(g,k,I2S(n))
        call StoreInteger(g,k,"n",n-1)
    endif
 set g=null
endfunction
function AttachedSetRemObject takes handle h, string setn, handle val returns nothing
    call AttachedSetRemInt(h,setn,CS_H2I(val))
endfunction

//============================================================================================================
function FromSetElement takes string setn, integer index returns string
 local gamecache g=CSCache()
    call StoreString(g,"argpass","set",setn)
    call StoreString(g,"argpass","seti",I2S(index))
 set g=null
 return ""
endfunction

//============================================================================================================
function ClearAttachedSet takes handle h, string setn returns nothing
    call FlushStoredMission(CSCache(),I2S(CS_H2I(h))+";"+setn)
endfunction

function CleanAttachedVars takes handle h returns nothing
 local gamecache g=CSCache()
 local string k=I2S(CS_H2I(h))
 local integer n=GetStoredInteger(g,k,"#totalsets")
 local integer i=1
    loop
        exitwhen i>n
        call FlushStoredMission(g,k+";"+GetStoredString(g,k,"#setName;"+I2S(i)))
        set i=i+1
    endloop
    call FlushStoredMission(g, k )
 set g=null
endfunction

function CleanAttachedVars_NoSets takes handle h returns nothing
    call FlushStoredMission(CSCache(), I2S(CS_H2I(h)) )
endfunction



//=============================================================================================
// Tables
//
// Tables are lame, the real name would be hash tables, they are just abbreviated usage
// of gamecache natives with the addition that you can also Copy the values of a table to
// another one, but don't expect it to be automatic, it must use a FieldData object to know
// which fields and of wich types to copy, Copying a table to another, with a lot of Fields,
// should surelly be lag friendly.
//
// The other thing about tables is that I can say that the Attached variables of a handle work
// inside a table and GetAttachmentTable which is just return bug and I2S , works to allow you
// to manipulate a handle's attached variables through a table.
//
// NewTable and DestroyTable were created to allow to create tables in the fly, but you can
// simply use strings for tables, but place the table names should be between "("")" for example
// "(mytable)" to avoid conflicts with other caster system stuff.
//
function NewTableIndex takes nothing returns integer
 local gamecache g=CSCache()
 local integer n=GetStoredInteger(g,"misc","FreeTableTotal")
 local integer i
     if (n>0) then
         set i=GetStoredInteger(g,"misc","FreeTable1")
         if (n>1) then
             call StoreInteger(g,"misc","FreeTable1", GetStoredInteger(g,"misc","FreeTable"+I2S(n)) )
             call FlushStoredInteger(g,"misc","FreeTable"+I2S(n))
         endif
         call StoreInteger(g,"misc","FreeTableTotal", n-1)
     else
         set i=GetStoredInteger(g,"misc","TableMaxReleasedIndex")+1
         call StoreInteger(g,"misc","TableMaxReleasedIndex",i)
     endif
     call StoreBoolean(g,"misc","Created"+I2S(i),true)

 set g=null
 return i
endfunction
function NewTable takes nothing returns string
    return I2S(NewTableIndex())
endfunction
function GetAttachmentTable takes handle h returns string
    return I2S(CS_H2I(h))
endfunction

//============================================================================================================
function DestroyTable takes string table returns nothing
 local gamecache g=CSCache()
 local integer i=S2I(table)
 local integer n
     if (i!=0) and (GetStoredBoolean(g,"misc","Created"+table)) then
         call FlushStoredBoolean(g,"misc","Created"+table)
         set n=GetStoredInteger(g,"misc","FreeTableTotal")+1
         call StoreInteger(g,"misc","FreeTableTotal",n)
         call StoreInteger(g,"misc","FreeTable"+I2S(n),i)
     endif
     call FlushStoredMission(g,table)
 set g=null
endfunction

//============================================================================================================
function ClearTable takes string table returns nothing
     call FlushStoredMission(CSCache(),table)
endfunction


//============================================================================================================
function SetTableInt takes string table, string field, integer val returns nothing
 local gamecache g=CSCache()
    if (val==0) then
        call FlushStoredInteger(g,table,field)
    else
        call StoreInteger(g,table,field,val)
    endif
 set g=null
endfunction
function GetTableInt takes string table, string field returns integer
    return GetStoredInteger(CSCache(),table,field)
endfunction

//============================================================================================================
function SetTableReal takes string table, string field, real val returns nothing
 local gamecache g=CSCache()
    if (val==0) then
        call FlushStoredReal(g,table,field)
    else
        call StoreReal(g,table,field,val)
    endif
 set g=null
endfunction
function GetTableReal takes string table, string field returns real
    return GetStoredReal(CSCache(),table,field)
endfunction

//============================================================================================================
function SetTableBoolean takes string table, string field, boolean val returns nothing
 local gamecache g=CSCache()
    if (not(val)) then
        call FlushStoredBoolean(g,table,field)
    else
        call StoreBoolean(g,table,field,val)
    endif
 set g=null
endfunction
function GetTableBoolean takes string table, string field returns boolean
    return GetStoredBoolean(CSCache(),table,field)
endfunction

//============================================================================================================
function SetTableString takes string table, string field, string val returns nothing
 local gamecache g=CSCache()
    if (val=="") or (val==null) then
        call FlushStoredString(g,table,field)
    else
        call StoreString(g,table,field,val)
    endif
 set g=null
endfunction
function GetTableString takes string table, string field returns string
    return GetStoredString(CSCache(),table,field)
endfunction

//============================================================================================================
// You may ask why am I using thousands of functions instead of multi-use return bug exploiters? Well,
// these make the thing much easier to read (in my opinion) and it is also better in performance since we
// have less function calls (H2U(GetTableObject("table","unit"))) would be worse than GetTableUnit that is
// quite direct.
//
function SetTableObject takes string table, string field, handle val returns nothing
  local gamecache g=CSCache()
    if (val==null) then
        call FlushStoredInteger(g,table,field)
    else
        call StoreInteger(g,table,field,CS_H2I(val))
    endif
 set g=null
endfunction
function GetTableObject takes string table, string field returns handle
    return GetStoredInteger(CSCache(),table,field)
    return null
endfunction
function GetTableWidget takes string table, string field returns widget
    return GetStoredInteger(CSCache(),table,field)
    return null
endfunction
function GetTableRect takes string table, string field returns rect
    return GetStoredInteger(CSCache(),table,field)
    return null
endfunction
function GetTableRegion takes string table, string field returns region
    return GetStoredInteger(CSCache(),table,field)
    return null
endfunction
function GetTableTimerDialog takes string table, string field returns timerdialog
    return GetStoredInteger(CSCache(),table,field)
    return null
endfunction
function GetTableUnit takes string table, string field returns unit
    return GetStoredInteger(CSCache(),table,field)
    return null
endfunction
function GetTableItem takes string table, string field returns item
    return GetStoredInteger(CSCache(),table,field)
    return null
endfunction
function GetTableEffect takes string table, string field returns effect
    return GetStoredInteger(CSCache(),table,field)
    return null
endfunction
function GetTableDestructable takes string table, string field returns destructable
    return GetStoredInteger(CSCache(),table,field)
    return null
endfunction
function GetTableTrigger takes string table, string field returns trigger
    return GetStoredInteger(CSCache(),table,field)
    return null
endfunction
function GetTableTimer takes string table, string field returns timer
    return GetStoredInteger(CSCache(),table,field)
    return null
endfunction
function GetTableGroup takes string table, string field returns group
    return GetStoredInteger(CSCache(),table,field)
    return null
endfunction
function GetTableTriggerAction takes string table, string field returns triggeraction
    return GetStoredInteger(CSCache(),table,field)
    return null
endfunction
function GetTableLightning takes string table, string field returns lightning
    return GetStoredInteger(CSCache(),table,field)
    return null
endfunction
function GetTableImage takes string table, string field returns image
    return GetStoredInteger(CSCache(),table,field)
    return null
endfunction
function GetTableUbersplat takes string table, string field returns ubersplat
    return GetStoredInteger(CSCache(),table,field)
    return null
endfunction
function GetTableSound takes string table, string field returns sound
    return GetStoredInteger(CSCache(),table,field)
    return null
endfunction

//============================================================================================================
// Returns true if the fiel contains a value different from 0, false,  null, or "" (depending on the type)
// it is worthless to use this with boolean, since it would be the same as reading the boolean value
//
function HaveSetField takes string table, string field, integer fieldType returns boolean
    if (fieldType == bj_GAMECACHE_BOOLEAN) then
        return HaveStoredBoolean(CSCache(),table,field)
    elseif (fieldType == bj_GAMECACHE_INTEGER) then
        return HaveStoredInteger(CSCache(),table,field)
    elseif (fieldType == bj_GAMECACHE_REAL) then
        return HaveStoredReal(CSCache(),table,field)
    elseif (fieldType == bj_GAMECACHE_STRING) then
        return HaveStoredString(CSCache(),table,field)
    endif
 return false
endfunction

//============================================================================================================
// Allows to copy a table to another one, but it needs a FieldData object to know which fields of which type
// it is supposed to copy.
//
function CopyTable takes integer FieldData, string sourceTable, string destTable returns nothing
 local gamecache g=CSCache()
 local integer i=1
 local string k=I2S(FieldData)
 local string k2
 local string k3
 local integer n=GetStoredInteger(g,k,"N")
 local integer t
    loop
        exitwhen (i>n)
        set k2=I2S(i)
        set t=GetStoredInteger(g,k,k2)
        set k3=GetStoredString(g,k,k2)
        if (t==bj_GAMECACHE_BOOLEAN) then
            if (HaveStoredBoolean(g,sourceTable,k3)) then
                call StoreBoolean(g,destTable,k3,GetStoredBoolean(g,sourceTable,k3))
            else
                call FlushStoredBoolean(g,destTable,k3)
            endif
        elseif (t==bj_GAMECACHE_INTEGER) then
            if (HaveStoredInteger(g,sourceTable,k3)) then
                call StoreInteger(g,destTable,k3,GetStoredInteger(g,sourceTable,k3))
            else
                call FlushStoredInteger(g,destTable,k3)
            endif
        elseif (t==bj_GAMECACHE_REAL) then
            if (HaveStoredReal(g,sourceTable,k3)) then
                call StoreReal(g,destTable,k3,GetStoredReal(g,sourceTable,k3))
            else
                call FlushStoredReal(g,destTable,k3)
            endif
        elseif (t==bj_GAMECACHE_STRING) then
            if (HaveStoredString(g,sourceTable,k3)) then
                call StoreString(g,destTable,k3,GetStoredString(g,sourceTable,k3))
            else
                call FlushStoredString(g,destTable,k3)
            endif
        endif
        set i=i+1
    endloop


 set g=null
endfunction

//=============================================================================================
// FieldData inherits from Table, was just designed to be used by CopyTable.
//
function FieldData_Create takes nothing returns integer
    return NewTableIndex()
endfunction

//============================================================================================================
// valueType uses the same integer variables from blizzard.j :
// bj_GAMECACHE_BOOLEAN, bj_GAMECACHE_INTEGER, bj_GAMECACHE_REAL and bj_GAMECACHE_STRING
//
function FieldData_AddField takes integer fielddata, string field, integer valueType returns nothing
 local gamecache g=CSCache()
 local string k=I2S(fielddata)
 local integer n=GetStoredInteger(g,k,"N")+1
 local string k2=I2S(n)

    call StoreString(g,k,k2,field)
    call StoreInteger(g,k,k2,valueType)
    call StoreInteger(g,k,"N",n)
 set g=null
endfunction

//=============================================================================================
// Destroys Field Data
function FieldData_Destroy takes integer fielddata returns nothing
    call DestroyTable(I2S(fielddata))
endfunction

//##End of CS Gamecache engine##
 
Level 39
Joined
Feb 27, 2007
Messages
5,036
Rather than trying to catch all the little things myself since you're only using CSCache you should be able to just replace it with Vexorian's faux-CSCache version for 1.24+. You will need the JASSNewGen pack to use this (just download the package, put in your wc3 directory, and use its new WE to open/save maps instead of the vanilla WE). Open my attached map and copy all 3 triggers over to yours. Delete the previous version of CSCache from your Custom Script section.

Finding a Vanilla WE solution to this will probably be difficult. And honestly Dr Super Good is right that you should really move to a different system that hasn't been outdated and deprecated.

Here's the full code of all 3 libraries required for CSCache to run properly in 1.24+:
JASS:
library CSData initializer init

    //****************************************************************************************************
    // CSData 16.0
    // ¯¯¯¯¯¯¯¯¯¯¯
    // CSDatas are like UserData in units and items, they now use a hashtable instead of big arrays,
    // should be faster and safer
    //
    // Notice that for public spells or systems to be distributed you should only use these
    // for private objects (those who the mapper would never have access to) If you are making something
    // for your map you can use them wherever you want.
    //
    // Best to be used in conjunction to CSArrays so you just specify an array id for a handle.
    //
    // DO NOT USE THIS ON THESE HANDLE TYPES: -lightning, -ubersplat, -image, -texttag,
    //                                        -any 'argument' handle (like playerstate, damagetype, etc)
    //
    //****************************************************************************************************

    //====================================================================================================
    globals
        private constant integer MAX_HANDLE_ID_COUNT = 408000
        // values lower than 8191: very fast, but very unsafe.
        // values bigger than 8191: not that fast, the bigger the number is the slower the function gets
        // Most maps don't really need a value bigger than 50000 here, but if you are unsure, leave it
        // as the rather inflated value of 408000
    endglobals

    //=================================================================================================
    // a.k.a H2I, changed name to CS_H2I to prevent conflicts with other systems, it then stayed that
    // instead of changing to a private or public function since many outside spells use it.
    //
    function CS_H2I takes handle h returns integer //For compat
        return GetHandleId(h)                      //
    endfunction                                    //

    //==================================================================================================
    globals
        private hashtable ht
    endglobals

    //It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
    function SetCSData takes handle h, integer v returns nothing
        call SaveInteger(ht , 0, GetHandleId(h) , v)
    endfunction

    function GetCSData takes handle h returns integer
        return LoadInteger( ht, 0, GetHandleId(h) )
    endfunction

    private function init takes nothing returns nothing
        set ht = InitHashtable()
    endfunction

endlibrary
JASS:
library CSSafeCache initializer init requires CSData
    //*************************************************************************************************
    //*                                                                                               *
    //* CSSafeCache 15.3                       http://wc3campaigns.net/vexorian                       *
    //* ¯¯¯¯¯¯¯                                                                                       *
    //*    CSCache is evil now, use just CSSafeCache when possible as it now contains all functions   *
    //* that do not use I2H or similar functions that are considered to cause unstability in the map  *
    //*                                                                                               *
    //*    CSCache has been deprecated, and is now unusuable since it can't be ported to patch 1.23b  *
    //* Migration from CSCache to CSSafeCache+structs should be easy                                  *
    //*                                                                                               *
    //* However, for old compatibility, CSSafeCache needs to stick to using gamecache, which is not   *
    //* as good as hashtable (it is slower and may be vulnerable to Sync attacks) I recommend         *
    //* switching from CSSafeCache to hashtable or Table                                              *
    //*                                                                                               *
    //*************************************************************************************************

    //=================================================================================================
    // CSSafeCache globals:
    //
    globals

        gamecache     cs_cache   = null
        integer array cs_array1
        integer array cs_array3
        integer array cs_array2
        private integer array cs_freeindexes
        private integer array cs_pairx
        private integer array cs_pairy
        private integer array cs_freepairs

    endglobals

    private function init takes nothing returns nothing
    // CSSafeCache initializer :
        call FlushGameCache(InitGameCache("cscache"))
        set cs_cache=InitGameCache("cscache")
        call ExecuteFunc("InitArrayIndexes")
        call ExecuteFunc("Pool_SetupCharMap")
    endfunction


    //=================================================================================================================
    // CS Pairs
    // ¯¯¯¯¯¯¯¯
    // This is a sub system to assist csarrays, you can use them but CSArrays of size 2 have the same functionality
    // although cspairs are be faster their space is limited and will start using gamecache if abused
    //
    function NewPair takes integer x, integer y returns integer
     local integer i
   
       if (cs_freepairs[0]==0) then
           set cs_freepairs[8190]=cs_freepairs[8190]+1
           set i= cs_freepairs[8190]
       else
           set i= cs_freepairs[cs_freepairs[0]]
           set cs_freepairs[0]=cs_freepairs[0]-1
       endif
       if (i>=8189) then //because we can only recycle up to 8189 (free pairs array uses indexes 0 and 8190 for other purposes)
           call StoreInteger(cs_cache,"pairx",I2S(i),x)
           call StoreInteger(cs_cache,"pairy",I2S(i),y)
       else
           set cs_pairx[i]=x
           set cs_pairy[i]=y

       endif
     return i
    endfunction

    function DestroyPair takes integer id returns nothing
        if (id>=8189) then
            call FlushStoredInteger(cs_cache,"pairx",I2S(id))
            call FlushStoredInteger(cs_cache,"pairy",I2S(id))
        else
            set cs_freepairs[0]=cs_freepairs[0]+1
            set cs_freepairs[cs_freepairs[0]] = id
        endif
    endfunction

    function SetPairXY takes integer id, integer x, integer y returns nothing
        if (id>=8189) then
            call StoreInteger(cs_cache,"pairx",I2S(id),x)
            call StoreInteger(cs_cache,"pairy",I2S(id),y)

        else
            set cs_pairx[id]=x
            set cs_pairy[id]=y
        endif
    endfunction

    function SetPairX takes integer id, integer x returns nothing
        if (id>=8189) then
            call StoreInteger(cs_cache,"pairx",I2S(id),x)
        else
            set cs_pairx[id]=x
        endif
    endfunction

    function SetPairY takes integer id, integer y returns nothing
        if (id>=8189) then
            call StoreInteger(cs_cache,"pairy",I2S(id),y)
        else
            set cs_pairy[id]=y
        endif
    endfunction

    function GetPairX takes integer id returns integer
        if (id>=8189) then
            return GetStoredInteger(cs_cache,"pairy",I2S(id))
        endif
     return cs_pairx[id]
    endfunction
    function GetPairY takes integer id returns integer
        if (id>=8189) then
            return GetStoredInteger(cs_cache,"pairx",I2S(id))
        endif
     return cs_pairy[id]
    endfunction





    //=================================================================================================================
    // CS Dynamic Arrays
    // ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    // Thanks: Pipedream, Peppar
    //
    // We can now create arrays in game! , also pass them as arguments or return values!
    // a 1 length array is a pointer!
    //
    function Array_TryFree takes nothing returns nothing
     local integer i
     local integer N=cs_array1[0]
     local integer k
     local boolean cleaned=false

     local integer loc

     local integer q
     local integer r
     


            set i=cs_array1[146]
            if (i>144) then
                call TimerStart(GetExpiredTimer(),60.,false,function Array_TryFree)
                return
            endif

            set loc=cs_freeindexes[i]
            set q=0
            loop
                exitwhen (loc==0)
           // we could just have used:
                    //set k=GetPairX(loc)
                    //set r=GetPairY(loc) But it is slower than direct usage:

                if (loc>=8192) then
               set k=GetStoredInteger(cs_cache,"pairx",I2S(loc))
            set r=GetStoredInteger(cs_cache,"pairy",I2S(loc))
           else
               set k=cs_pairx[loc]
            set r=cs_pairy[loc]
           endif

                if (k+i-1==N) then


                    //we found one we can remove from the list

                    set cleaned=true
                    //decrement N
                 
                    set N=k-2

                    //Remove from the list:
                    if (q==null) then
                        //That was the first, update the array as well
                        set cs_freeindexes[i]=r
                    else
                        //Update the next of the previous one
                        //We could use : call SetPairY(q,,r) but it is slower
               if (q>=8189) then
                   call StoreInteger(cs_cache,"pairy",I2S(q),r)
                        else
                set cs_pairy[q]=r
               endif


                    endif
                    if (r==null) then
                        //This was the last one, update it in the array as well
                        set cs_freeindexes[i+4096]=q
                    endif
                 
                    call DestroyPair(loc)
                    set loc=q
                endif
                set q=loc
                set loc=r
            endloop
         
            if (cleaned) then
                set cs_array1[0]=N
                set cs_array1[146]=1
            else
                set cs_array1[146]=cs_array1[i+1]
            endif
     call TimerStart(GetExpiredTimer(),0.2,false,function Array_TryFree)

    endfunction

    function InitArrayIndexes2 takes nothing returns nothing
     local integer i=0
        loop
            exitwhen (i==8191)
            set cs_pairx[i]=777
            set cs_pairy[i]=777
            set i=i+1
        endloop
   
    endfunction


    function InitArrayIndexes takes nothing returns nothing
     local integer i=0
     local integer a=1
     local integer b=1
     local integer c


        //By placing 777 there instead of 0 it is easier to recognize non correctly initialized bugs
        loop
            exitwhen (i== 8191)
            set cs_array1[i]=777
            set cs_array2[i]=777
            set cs_array3[i]=777
            //set cs_pairx[i]=777
            //set cs_pairy[i]=777
            set i=i+1
        endloop
        call ExecuteFunc("InitArrayIndexes2")


        set cs_freeindexes[0]=0 //The stack for the table indexes.
        set cs_freepairs[0]=0
        set cs_freepairs[8190]=0
        set i=1
        loop
            set c=a+b
            set a=b
            set b=c
            exitwhen (b>144) //max size is 144
            set cs_freeindexes[b]=0 //the first of the list
            set cs_freeindexes[b+4096]=0 //the last of the list
            loop
                exitwhen (i>b)
                set cs_array1[i]=b
                set i=i+1
            endloop
         
        endloop
        set cs_array1[i]=b //i is 145
        set cs_array1[146]=1
        set cs_array1[147]=101 //initial table index is 101
        set cs_array1[0]=147
        //index 0: Last used index
        // 1 to 145 : Fibonacci sequence
        // 146 : last check
        // 147 : Table indexes check
     
        //This has a good chance to compress the thing when necesary
        call TimerStart(CreateTimer(),60.,false,function Array_TryFree)

    endfunction

    //=============================================================================================
    // Create an array of size, max size is 144, if doinit is true it will put a bunch of zeros
    // in the indexes
    //
    function NewArray takes integer size, boolean doinit returns integer
     local integer i
     local integer rsize=cs_array1[size]
     local integer loc

        set loc=cs_freeindexes[rsize]
        if (loc!=0) then

            set cs_freeindexes[rsize]= GetPairY(loc)
            if (cs_freeindexes[rsize]==0) then
                set cs_freeindexes[4096+rsize]=0
            endif
            set i=GetPairX(loc)
            call DestroyPair(loc)

            if (i==0) then
                //this code was probably a good idea when we used locations for the free indexes list, now we use pairs which should not ever
                //do this unless someone modiffied the pair array incorrectly
         
                call BJDebugMsg("Caster System: Unexpected error (5): corrupt stack, attempt to recover "+I2S(rsize))
                // recovering involves forgetting about the stack which got corrupted and start again from zero, it will leak
                // and probably get slow due to usage of gamecache but it is better than the problems that a corrupt stack might cause
                set cs_freeindexes[rsize]=0
                set cs_freeindexes[4096+rsize]=0
             
             
                return NewArray(size,doinit)
            endif

        else
         
            //sz  i   i+1   i+2
            //[ ][   ][    ][   ]
     
            set i=cs_array1[0]+2
            set cs_array1[0]=i+rsize-1

        endif

        //It used to store size in the index equal to the array's id
        // but that required the get/set functions to increment 1 in every index
        // calculation. Instead, making size the previous index to the array works
        if (i<=8191) then
            set cs_array1[i-1]=size
        elseif (i<=16382) then
            set cs_array2[i-8192]=size
        else
            call StoreInteger(cs_cache,I2S(-i),"size",size)
        endif

     
        if (not doinit) then
            return i
        endif
     
        //    3
        //[i][i+1][i+2]

        set size=i+size-1
        if (size>=16382) then
            set size=16381
        endif
        loop
            exitwhen (size<i) or (size<8191)
            set cs_array2[size-8191]=0
            set size=size-1
        endloop
        loop
            exitwhen (size<i)
            set cs_array1[size]=0
            set size=size-1
        endloop
     
        //call DisplayTextToPlayer(GetLocalPlayer(),0,0,I2S(i))

     return i
    endfunction


    //===============================================================================================================
    // Remember to destroy arrays when you no longer need them, else new arrays will get slower after a bunch of
    // arrays are active
    //
    function DestroyArray takes integer id returns nothing
     local integer L
     local integer loc
    // local string k=I2S(-id)
     local integer lstpace

        if (id<=8191) then
            set L=cs_array1[cs_array1[id-1]]
        elseif (id<=16382) then
            set L=cs_array1[cs_array2[id-8191]]
        else
            set L=cs_array1[GetStoredInteger(cs_cache,I2S(-id),"size")]
            //No way you are gonna call DestroyArray without calling
           //NewArray first, so we can use the gamecache variable directly instead

        endif
        set lstpace=id+L-1
       call FlushStoredMission(cs_cache,I2S(-id))
   
       if (lstpace>16382) then
     
           if (lstpace==cs_array1[0]) then
               //We just destroyed the array that happens to be at the end of the heap.
               //Just get it back
               set cs_array1[0]=id-2
               set cs_array1[146]=1
           else
               //Add to the last
               set loc=cs_freeindexes[L+4096]
               if (loc==0) then
                   set loc=NewPair(id,0)
                   set cs_freeindexes[L]=loc
                   set cs_freeindexes[L+4096]=loc
               else
                   set cs_freeindexes[L+4096]=  NewPair(id,0)
                   //we could just use: call SetPairY(loc, cs_freeindexes[L+4096] ) //but that's slower
                   if (loc>=8189) then
                  call StoreInteger(cs_cache,"pairy",I2S(loc), cs_freeindexes[L+4096] )
                   else
                       set cs_pairy[loc]=cs_freeindexes[L+4096]             
                   endif

               endif


           endif

       elseif (lstpace==cs_array1[0]) then
               //We just destroyed the array that happens to be at the end of the heap.
               //Just get it back
               set cs_array1[0]=id-2
               set cs_array1[146]=1
       else
           set loc=cs_freeindexes[L]
           set cs_freeindexes[L]=NewPair(id,loc)

           if (loc==0) then
               set cs_freeindexes[L+4096]=cs_freeindexes[L]
           endif

       endif

    endfunction

    //================================================================================================================
    // Int Set/Get array usage prototypes.
    //
    // These are the actual functions, the rest are just the result of copy paste, if these functions are updated
    // the other ones should be updated as well (They are just return bugged variations)
    //
    function SetArrayInt takes integer id, integer index, integer val returns nothing
        set index=id+index
        if (index<8191) then
            set cs_array1[index]=val
        elseif (index<16382) then  
            set cs_array2[index-8191]=val
        else
            call StoreInteger(cs_cache,I2S(-id),I2S(index),val)
        endif
    endfunction
    function GetArrayInt takes integer id, integer index returns integer
        set index=id+index
        if (index<8191) then
            return cs_array1[index]
        elseif (index<16382) then  
            return cs_array2[index-8191]
        endif
     return GetStoredInteger(cs_cache,I2S(-id),I2S(index))
    endfunction


    //================================================================================================================
    // String Set/Get array
    //
    // Due String related return bug issues, these are forced to use gamecache
    //
    function SetArrayString takes integer id, integer index, string val returns nothing
        call StoreString(cs_cache,I2S(-id),I2S(index),val)
    endfunction
    function GetArrayString takes integer id, integer index returns string
     return GetStoredString(cs_cache,I2S(-id),I2S(index))
    endfunction

    //(Boolean is not needed)

    //==========================================================================================================================
    // Returns the size of an array (the specified by player one, not the actual size of it) should be useful.
    //
    function GetArraySize takes integer id returns integer
        if (id<=8191) then
            return cs_array1[id-1]
        elseif (id<=16382) then
            return cs_array2[id-8192]
        endif
     return GetStoredInteger(cs_cache,I2S(-id),"size")
    endfunction


    //===========================================================================================================================
    // Returns an array that is an exact copy of the given array
    //
    function CloneArray takes integer id returns integer
     local integer sz
     local integer i
     local integer sz2
     local integer x
     local integer y

        if (id<=8191) then
            set sz=cs_array1[id-1]
        elseif (id<=16382) then
            set sz=cs_array2[id-8192]
        else
            set sz=GetStoredInteger(cs_cache,I2S(-id),"size")
            //No way you are gonna call DestroyArray without calling
           //NewArray first, so we can use the gamecache variable directly instead

        endif

        set i=NewArray(sz,false)
     
        set sz2=i+sz-1
        set sz=id+sz-1
        set x=i
        set y=id
        loop
            exitwhen ((y>sz) or (y>=8191) or (x>=8191))
            set cs_array1[x]=cs_array1[y]
            set y=y+1
            set x=x+1
        endloop
        loop
            exitwhen ((y>sz) or (y>=8191) or (x>=16382))
            set cs_array2[x-8191]=cs_array1[y]
            set y=y+1
            set x=x+1
        endloop
        loop
            exitwhen ((y>sz) or (y>=8191))
            call StoreInteger(cs_cache,I2S(-i),I2S(x-i),cs_array1[y])
            set x=y+1
            set y=y+1
        endloop
        //...
        loop
            exitwhen ((y>sz) or (y>=16382) or (x>=8191))
            set cs_array1[x]=cs_array2[y-8191]
            set y=y+1
            set x=x+1
        endloop
        loop
            exitwhen ((y>sz) or (y>=16382) or (x>=16382))
            set cs_array2[x-8191]=cs_array2[y-8191]
            set y=y+1
            set x=x+1
        endloop
        loop
            exitwhen ((y>sz) or (y>=16382))
            call StoreInteger(cs_cache,I2S(-i),I2S(x-i),cs_array2[y-8191])
            set y=y+1
            set x=x+1
        endloop
        //...
        loop
            exitwhen ((y>sz) or (x>=8191))
            set cs_array1[x]=GetStoredInteger(cs_cache,I2S(-id),I2S(y-id))
            set y=y+1
            set x=x+1
        endloop
        loop
            exitwhen ((y>sz) or (x>=16382))
            set cs_array2[x-8191]=GetStoredInteger(cs_cache,I2S(-id),I2S(y-id))
            set y=y+1
            set x=x+1
        endloop
        loop
            exitwhen (y>sz)
            call StoreInteger(cs_cache,I2S(-i),I2S(x-i),GetStoredInteger(cs_cache,I2S(-id),I2S(y-id)))
            set y=y+1
            set x=x+1
        endloop
     
     return i
    endfunction

    //==================================================================================================
    // Attachable vars : Attacheable variables are what most other people call Handle Variables, they
    // allow to relate data with any handle, using a label, and its value, the stuff auto flushes if
    // the value is 0, false, "", or null .
    //
    // Differences between Attacheable variables and "Local Handle Variables" :
    // - The names of the functions
    // - The name of the function group does not cause confusion, it is difficult to say:
    //   "you should set local handle variables to null at the end of a function" since
    //   it sounds as if you were talking about the "Local Handle Variables"
    // - And can work together with Tables.
    //
    // Gamecache stuff are NOT Case Sensitive, don't ever use "" for label (Crashes game!)
    //
    //
    // Although locations and dynamic arrays are faster than gamecache, gamecache still keeps the flexibility
    // Best thing to do in my opinion is to combine these options. By combining you can acquire gamecache
    // flexibility and arrays/locs speed to solve a problem
    //

    //============================================================================================================
    // For integers
    //
    function AttachInt takes handle h, string label, integer x returns nothing
        if x==0 then
            call FlushStoredInteger(cs_cache,I2S(GetHandleId(h)),label)
        else
            call StoreInteger(cs_cache,I2S(GetHandleId(h)),label,x)
        endif
    endfunction
    function GetAttachedInt takes handle h, string label returns integer
        return GetStoredInteger(cs_cache, I2S(GetHandleId(h)), label)
    endfunction

    //=============================================================================================================
    function AttachReal takes handle h, string label, real x returns nothing
         if x==0 then
            call FlushStoredReal(cs_cache,I2S(GetHandleId(h)),label)
        else
            call StoreReal(cs_cache,I2S(GetHandleId(h)),label,x)
        endif
    endfunction
    function GetAttachedReal takes handle h, string label returns real
        return GetStoredReal(cs_cache,I2S(GetHandleId(h)),label)
    endfunction

    //=============================================================================================================
    function AttachBoolean takes handle h, string label, boolean x returns nothing
        if not x then
            call FlushStoredBoolean(cs_cache,I2S(GetHandleId(h)),label)
        else
            call StoreBoolean(cs_cache,I2S(GetHandleId(h)),label,x)
        endif
    endfunction
    function GetAttachedBoolean takes handle h, string label returns boolean
        return GetStoredBoolean(cs_cache,I2S(GetHandleId(h)),label)
    endfunction

    //=============================================================================================================
    function AttachString takes handle h, string label, string x returns nothing
        if ((x=="") or (x==null)) then
            call FlushStoredString(cs_cache,I2S(GetHandleId(h)),label)
        else
            call StoreString(cs_cache,I2S(GetHandleId(h)),label,x)
        endif
    endfunction
    function GetAttachedString takes handle h, string label returns string
        return GetStoredString(cs_cache,I2S(GetHandleId(h)),label)
    endfunction

    //============================================================================================================
    function CleanAttachedVars takes handle h returns nothing
        call FlushStoredMission(cs_cache,I2S(GetHandleId(h)))
    endfunction

    //============================================================================================================
    // Left for compat
    function CleanAttachedVars_NoSets takes handle h returns nothing
        call FlushStoredMission(cs_cache,I2S(GetHandleId(h)))
    endfunction


    //=============================================================================================
    // Tables
    //
    // Tables are lame, the real name would be hash tables, they are just abbreviated usage
    // of gamecache natives with the addition that you can also Copy the values of a table to
    // another one, but don't expect it to be automatic, it must use a FieldData object to know
    // which fields and of wich types to copy, Copying a table to another, with a lot of Fields,
    // should surelly be lag friendly.
    //
    // The other thing about tables is that I can say that the Attached variables of a handle work
    // inside a table and GetAttachmentTable which is just return bug and I2S , works to allow you
    // to manipulate a handle's attached variables through a table.
    //
    // NewTable and DestroyTable were created to allow to create tables in the fly, but you can
    // simply use strings for tables, but place the table names should be between "("")" for example
    // "(mytable)" to avoid conflicts with other caster system stuff.
    //
    function NewTableIndex takes nothing returns integer
     local integer loc=cs_freeindexes[0]
     local integer i
        if (loc!=0) then
            set i=GetPairX(loc)
            set cs_freeindexes[0]=GetPairY(loc)
            call DestroyPair(loc)

            return i
        endif
        set i=cs_array1[147]+1
        set cs_array1[147]=i

     return i
    endfunction
    function NewTable takes nothing returns string
     local integer loc=cs_freeindexes[0]
     local integer i
        if (loc!=0) then
            set i=GetPairX(loc)
            set cs_freeindexes[0]=GetPairY(loc)
            call DestroyPair(loc)

            return I2S(i)
        endif
        set i=cs_array1[147]+1
        set cs_array1[147]=i

     return I2S(i)
    endfunction
    function GetAttachmentTable takes handle h returns string
        return I2S(GetHandleId(h))
    endfunction

    //============================================================================================================
    function DestroyTable takes string table returns nothing
     local integer i=S2I(table)
     local integer n
         call FlushStoredMission(cs_cache,table)
         if ((i>100) and (i<1000000)) then //All right, more than 1000000 tables is just wrong.
             if (i==cs_array1[147]) then
                 set cs_array1[147]=cs_array1[147]-1
             else
                 set cs_freeindexes[0]=  NewPair(i,cs_freeindexes[0])
             endif
         endif

    endfunction

    //============================================================================================================
    function ClearTable takes string table returns nothing
         call FlushStoredMission(cs_cache,table)
    endfunction


    //============================================================================================================
    function SetTableInt takes string table, string field, integer val returns nothing
        if (val==0) then
            call FlushStoredInteger(cs_cache,table,field)
        else
            call StoreInteger(cs_cache,table,field,val)
        endif
    endfunction
    function GetTableInt takes string table, string field returns integer
        return GetStoredInteger(cs_cache,table,field)
    endfunction

    //============================================================================================================
    function SetTableReal takes string table, string field, real val returns nothing
        if (val==0) then
            call FlushStoredReal(cs_cache,table,field)
        else
            call StoreReal(cs_cache,table,field,val)
        endif
    endfunction
    function GetTableReal takes string table, string field returns real
        return GetStoredReal(cs_cache,table,field)
    endfunction

    //============================================================================================================
    function SetTableBoolean takes string table, string field, boolean val returns nothing
        if (not(val)) then
            call FlushStoredBoolean(cs_cache,table,field)
        else
            call StoreBoolean(cs_cache,table,field,val)
        endif
    endfunction
    function GetTableBoolean takes string table, string field returns boolean
        return GetStoredBoolean(cs_cache,table,field)
    endfunction

    //============================================================================================================
    function SetTableString takes string table, string field, string val returns nothing
        if (val=="") or (val==null) then
            call FlushStoredString(cs_cache,table,field)
        else
            call StoreString(cs_cache,table,field,val)
        endif
    endfunction
    function GetTableString takes string table, string field returns string
        return GetStoredString(cs_cache,table,field)
    endfunction

    //============================================================================================================
    // Returns true if the fiel contains a value different from 0, false,  null, or "" (depending on the type)
    // it is worthless to use this with boolean, since it would be the same as reading the boolean value
    //
    function HaveSetField takes string table, string field, integer fieldType returns boolean
        if (fieldType == bj_GAMECACHE_BOOLEAN) then
            return HaveStoredBoolean(cs_cache,table,field)
        elseif (fieldType == bj_GAMECACHE_INTEGER) then
            return HaveStoredInteger(cs_cache,table,field)
        elseif (fieldType == bj_GAMECACHE_REAL) then
            return HaveStoredReal(cs_cache,table,field)
        elseif (fieldType == bj_GAMECACHE_STRING) then
            return HaveStoredString(cs_cache,table,field)
        endif
     return false
    endfunction

    //============================================================================================================
    // Allows to copy a table to another one, but it needs a FieldData object to know which fields of which type
    // it is supposed to copy.
    //
    function CopyTable takes integer FieldData, string sourceTable, string destTable returns nothing
     local integer i=1
     local string k=I2S(FieldData)
     local string k2
     local string k3
     local integer n=GetStoredInteger(cs_cache,k,"N")
     local integer t
        loop
            exitwhen (i>n)
            set k2=I2S(i)
            set t=GetStoredInteger(cs_cache,k,k2)
            set k3=GetStoredString(cs_cache,k,k2)
            if (t==bj_GAMECACHE_BOOLEAN) then
                if (HaveStoredBoolean(cs_cache,sourceTable,k3)) then
                    call StoreBoolean(cs_cache,destTable,k3,GetStoredBoolean(cs_cache,sourceTable,k3))
                else
                    call FlushStoredBoolean(cs_cache,destTable,k3)
                endif
            elseif (t==bj_GAMECACHE_INTEGER) then
                if (HaveStoredInteger(cs_cache,sourceTable,k3)) then
                    call StoreInteger(cs_cache,destTable,k3,GetStoredInteger(cs_cache,sourceTable,k3))
                else
                    call FlushStoredInteger(cs_cache,destTable,k3)
                endif
            elseif (t==bj_GAMECACHE_REAL) then
                if (HaveStoredReal(cs_cache,sourceTable,k3)) then
                    call StoreReal(cs_cache,destTable,k3,GetStoredReal(cs_cache,sourceTable,k3))
                else
                    call FlushStoredReal(cs_cache,destTable,k3)
                endif
            elseif (t==bj_GAMECACHE_STRING) then
                if (HaveStoredString(cs_cache,sourceTable,k3)) then
                    call StoreString(cs_cache,destTable,k3,GetStoredString(cs_cache,sourceTable,k3))
                else
                    call FlushStoredString(cs_cache,destTable,k3)
                endif
            endif
            set i=i+1
        endloop


    endfunction

    //=============================================================================================
    // FieldData inherits from Table, was just designed to be used by CopyTable.
    //
    function FieldData_Create takes nothing returns integer
        return NewTableIndex()
    endfunction


    //============================================================================================================
    // valueType uses the same integer variables from blizzard.j :
    // bj_GAMECACHE_BOOLEAN, bj_GAMECACHE_INTEGER, bj_GAMECACHE_REAL and bj_GAMECACHE_STRING
    //
    function FieldData_AddField takes integer fielddata, string field, integer valueType returns nothing
     local string k=I2S(fielddata)
     local integer n=GetStoredInteger(cs_cache,k,"N")+1
     local string k2=I2S(n)

        call StoreString(cs_cache,k,k2,field)
        call StoreInteger(cs_cache,k,k2,valueType)
        call StoreInteger(cs_cache,k,"N",n)

    endfunction

    //=============================================================================================
    // Destroys Field Data
    function FieldData_Destroy takes integer fielddata returns nothing
        call DestroyTable(I2S(fielddata))
    endfunction

    //=============================================================================================
    // Pools
    //
    // A better name for pools would be sets, but by the time I made them I couldn't think of that
    // name, besides the word set is already a JASS syntax word so it would have been problematic.
    //
    // Another naming failure is that values of a pool are called "items" but that conflicts with
    // the word item that points to wc3 items, Pools can only store integer values, but if you want
    // you can go and use the return bug on them.
    //

    function CreatePool takes nothing returns integer
     local integer i=NewArray(34,false)
        call SetArrayInt(i,0,0)
     return i
    endfunction

    function ClearPool takes integer poolid returns nothing
        call SetArrayInt(poolid,0,0) //[0:integer:n]
        call FlushStoredMission(cs_cache,I2S(-poolid))
    endfunction

    function DestroyPool takes integer poolid returns nothing
        call DestroyArray(poolid)
    endfunction


    function PoolAddItem takes integer poolid, integer value returns nothing
     local integer n
     local string k=I2S(-poolid)
     local string vk="#"+I2S(value)
        if not HaveStoredInteger(cs_cache,k,vk) then
            set n=GetArrayInt(poolid,0)+1 //[0:integer:N]
            call StoreInteger(cs_cache,k,vk,n)
            if (n>33) then
                call StoreInteger(cs_cache,k,I2S(n),value)
            else
                call SetArrayInt(poolid,n,value)
            endif
            call SetArrayInt(poolid,0,n) //[0:integer:N]
        endif
    endfunction



    function PoolRemoveItem takes integer poolid, integer value returns nothing
     local string k=I2S(-poolid)
     local string vk="#"+I2S(value)

     local integer p=GetStoredInteger(cs_cache,k,vk)
     local integer n

        if (p!=0) then
            set n=GetArrayInt(poolid,0) //[0:integer:N]
            call FlushStoredInteger( cs_cache, k, vk)
         
            if (n>p) then
         
                if (n>33) then
                    set vk=I2S(n)
                    set value=GetStoredInteger(cs_cache,k,vk)
                    call FlushStoredInteger(cs_cache,k,vk)
                else
                    set value=GetArrayInt(poolid,n)
                endif
                call StoreInteger(cs_cache,k,"#"+I2S(value),p)
                if (p>33) then
                    call StoreInteger(cs_cache,k,I2S(p),value)
                else
                    call SetArrayInt(poolid,p,value)
                endif
            elseif (p>33) then
                call FlushStoredInteger(cs_cache,k,I2S(p))
            endif
            call SetArrayInt( poolid,0,n - 1) //[0:integer:N]
        endif
    endfunction

    //===================================================================================
    function PoolGetItem takes integer poolid, integer itemn returns integer
        if (itemn>33) then
            return GetStoredInteger( cs_cache, I2S(-poolid), I2S(itemn))
        endif
     return GetArrayInt(poolid,itemn)
    endfunction

    //===================================================================================
    function CountItemsInPool takes integer poolid returns integer
     return GetArrayInt(poolid,0) //[0:integer:N]
    endfunction


    //===================================================================================
    // Removed : GetEnumPoolItem , ForPool and ForPool2 they are much worse than just
    // using CountItemsInPool and PoolGetItem to iterate the pool
    //

    //===================================================================================
    function GetFirstOfPool takes integer poolid returns integer
        return GetArrayInt(poolid,1) //[1 is just the first of the pool]
    endfunction


    //===================================================================================
    function PoolPickRandomItem takes integer poolid returns integer
     local integer p=GetRandomInt(  1, GetArrayInt(poolid,0) )
        if (p>33) then
            return GetStoredInteger( cs_cache, I2S(-poolid), I2S(p))
        endif
     return GetArrayInt(poolid,p)
    endfunction

    //===================================================================================
    function GetItemPositionInPool takes integer poolid, integer it returns integer
        return GetStoredInteger( cs_cache, I2S(-poolid), "#"+I2S(it))
    endfunction

    //===================================================================================
    function IsItemInPool takes integer poolid, integer it returns boolean
        return(HaveStoredInteger( cs_cache, I2S(-poolid), "#"+I2S(it)) )
    endfunction

    //===================================================================================
    // This had to be optimized for speed, if it was just a loop using the above functions
    // that would have been too slow to be worth keeping. That's a bad thing about JASS
    // it is such an slow language that code reusability always has the cost of speed
    //
    function PoolAddPool takes integer sourcepoolid, integer destpoolid returns nothing
     local integer a=1
     local integer n=GetArrayInt( sourcepoolid,0) //[0:integer:N]
     local integer dn=GetArrayInt( destpoolid,0) //[0:integer:N]
     local string sk=I2S(-sourcepoolid)

     local string k=I2S(-destpoolid)
     local integer v
     local string vk

        loop
            exitwhen (a>n)
            if (a>33) then
                set v=GetStoredInteger(cs_cache,sk,I2S(a))
            else
                set v=GetArrayInt(sourcepoolid,a)
            endif
            set vk="#"+I2S(v)
            if not HaveStoredInteger(cs_cache,k,vk) then
                set dn=dn+1
                call StoreInteger(cs_cache,k,vk,dn)
                if (dn>33) then
                    call StoreInteger(cs_cache,k,I2S(dn),v)
                else
                    call SetArrayInt(destpoolid,dn,v)
                endif
            endif
            set a=a+1
        endloop
        call SetArrayInt(destpoolid,0,dn) //[0:integer:N]
    endfunction

    //=============================================================================================
    // Oh darn, After making PoolAddPool I don't feel like writting this one
    // All right I am at least make the get code
    //
    function PoolRemovePool takes integer sourcepoolid, integer destpoolid returns nothing
     local integer a=1
     local integer n=GetArrayInt( sourcepoolid,0) //[0:integer:N]
     local integer v
     local string sk=I2S(-sourcepoolid)
        loop
            exitwhen a>n
            if (a>33) then
                set v=GetStoredInteger(cs_cache,sk,I2S(a) )
            else
                set v=GetArrayInt(sourcepoolid,a)
            endif
            call PoolRemoveItem( destpoolid, v)
            set a=a+1
        endloop
    endfunction

    //===================================================================================
    // Adds a tokenized string to a pool,
    //  Example: PoolAddS(udg_p, "1;2;3;4") will add to the udg_p pool : 1,2,3 and 4
    //
    function PoolAddS takes integer poolid, string s returns nothing
     local integer i=0
     local integer st
     local string c
        set s=s+";"
        set st=0
        loop
            set c=SubString(s, i, i+1)
            exitwhen (c == "") or (c == null)
            if (c == ";") then
                call PoolAddItem( poolid, S2I(SubString(s, st, i)) )
                set st=i+1
            endif
            set i=i+1
        endloop
    endfunction

    //===================================================================================
    // Converts a tokenized string into a pool,
    //  Example: S2Pool( "1;2;3;4") will return a pool that has 1,2,3 and 4 inside
    //
    function S2Pool takes string s returns integer
     local integer spool= CreatePool()
        call PoolAddS(spool,s)
     return spool
    endfunction

    //===================================================================================
    // Does the opposite of S2Pool, debugging is a good use for this function.
    //
    function Pool2S takes integer P returns string
     local integer N=CountItemsInPool(P)
     local integer i
     local string s
        if (N>=1) then
            set s=I2S(PoolGetItem(P,1) )
            set i=2
        else
            return ""
        endif
        loop
            exitwhen (i>N)
            set s=s+";"+I2S(PoolGetItem(P,i))
            set i=i+1
        endloop
    return s
    endfunction

    //=============================================================================================================
    // Fixes a lame bug by blizzard related to the custom script section (The condition of the if statement might
    // actually be true.
    //
    function Pool_Percent takes nothing returns string
       if ("%"=="") then
           return "%%"
       endif
     return "%"
    endfunction

    function Pool_SetupCharMap takes nothing returns nothing
     local string cm=".................................!.#$"+Pool_Percent()+"&'()*+,-./0123456789:;<=>.@ABCDEFGHIJKLMNOPQRSTUVWXYZ[.]^_`abcdefghijklmnopqrstuvwxyz{|}~................................................................................................................................."
     local integer i=0
     local string c
        if HaveStoredInteger(cs_cache,"charmap_upper","A") then
            return
        endif
        loop
            set c=SubString(cm,i,i+1)
            exitwhen (c==null) or (c=="")
            if (c!=".") then
                if c==StringCase(c,true) then
                    call StoreInteger(cs_cache,"charmap_upper",c,i)
                else
                    call StoreInteger(cs_cache,"charmap_lower",c,i)
                endif
             
            endif
            set i=i+1
        endloop

    endfunction


    function Pool_Rawcode2Int takes string s returns integer
     local string c
     local integer i=0
     local integer r=0
        loop
            exitwhen i>3
            set c=SubString(s,i,i+1)
            set r=r*256
            if c==StringCase(c,true) then
                set r=r+GetStoredInteger(cs_cache,"charmap_upper",c)
            else
                set r=r+GetStoredInteger(cs_cache,"charmap_lower",c)
            endif
         
            set i=i+1
        endloop
     return r
    endfunction

    function PoolAddRawcodes_thread takes nothing returns nothing
    //Threaded because I don't want it to halt execution for no reason
    //
     local string s=bj_lastPlayedMusic
     local integer poolid=bj_groupEnumTypeId
     local string c
     local integer i=0
     local integer st=0
        set s=s+";"
        loop
            set c=SubString(s, i, i+1)
            exitwhen (c == "") or (c == null)
            if c == ";" then
                call PoolAddItem(poolid, Pool_Rawcode2Int(SubString(s,st,i) ))
                set st=i+1
            endif
            set i=i+1
        endloop

    endfunction

    //=====================================================================================================================
    // Adds a string of tokenized rawcodes to a pool
    //  Example: PoolAddRawcodes(udg_p,"A000;A001")  will add 'A000' and 'A001' to the pool
    //
    // (Saves some lines, but is not as good efficiency wise)
    //
    function PoolAddRawcodes takes integer poolid, string s returns nothing
     local string b=bj_lastPlayedMusic
        set bj_groupEnumTypeId=poolid
        set bj_lastPlayedMusic=s
        call ExecuteFunc("PoolAddRawcodes_thread")
        set bj_lastPlayedMusic=b
    endfunction

    //===================================================================================================================
    // Converts a tokenized string of rawcodes into a pool,
    //  Example: Rawcodes2Pool( "A000;A001;AHbz;S000") will return a pool that has 'A000,'A001','AHbx' and 'S000' inside
    //
    // (Saves some lines, but is not as good efficiency wise)
    //
    function Rawcodes2Pool takes string s returns integer
     local integer spool= CreatePool()
        call PoolAddRawcodes(spool,s)
     return spool
    endfunction

    //===================================================================================================================
    // A subproduct of the Pool's Rawcode support is that we can easily have this function so I am including it even if
    // it has nothing to do with data storage.
    //
    // takes "Aloc" and converts it into 'Aloc'
    // it is different to the Pool_Rawcode2Int function in that it is safe to use it when it is the first CSCache
    // function ever used. But it is a little slower (wc3mapoptimizer should make it as fast though)
    //
    function CS_Rawcode2Int takes string s returns integer
     local string c
     local integer i=0
     local integer r=0
        loop
            exitwhen i>3
            set c=SubString(s,i,i+1)
            set r=r*256
            if c==StringCase(c,true) then
                set r=r+GetStoredInteger(cs_cache,"charmap_upper",c)
            else
                set r=r+GetStoredInteger(cs_cache,"charmap_lower",c)
            endif
         
            set i=i+1
        endloop
     return r
    endfunction


endlibrary
JASS:
/*******************************************************************************************
 Faux CSCache AttachVars
----------------
    Do not use these functions for new stuff, it is just not the right thing to do...

    The intention of Faux CSCache  is to be a patch fix for a map's migration to
  patch 1.24 This library might not cover all uses of handle vars, some of them are just
  impossible with the new rules set in patches 1.23b and 1.24, but they should work for
  most of the cases....

*******************************************************************************************/

//==========================================================================================
library CSCache initializer init requires CSSafeCache

globals
    private hashtable ht
endglobals


    // too bad the Handle vars' old functionality forces me to make these things
    // inline-unfriendly
    function SetTableObject takes string subject,  string label, agent value returns nothing
        if(value==null) then
            call RemoveSavedHandle( ht, StringHash(subject), StringHash(label))
        else
            call SaveAgentHandle( ht, StringHash(subject), StringHash(label), value)
        endif
    endfunction




    function GetTableHandle takes string subject, string label returns agent
        debug call BJDebugMsg("[debug] What the heck? Why would you call HandleHandle I guess this was caused by a search and replace mistake")
        return null
    endfunction

    // these are inline friendly, ok, maybe they aren't because jasshelper does not recognize
    // GetHandleId as non-state changing. But they will be once I fix jasshelper...



    // got bored so I now use a textmacro...
    //! textmacro FAUX_HANDLE_VARS_GetTableHandle takes NAME, TYPE
         function SetTable$NAME$ takes string subject,  string label, $TYPE$ value returns nothing
             if(value==null) then
                call RemoveSavedHandle( ht, StringHash(subject), StringHash(label))
             else
                call Save$NAME$Handle( ht, StringHash(subject), StringHash(label), value)
             endif
         endfunction

         function GetTable$NAME$ takes string subject, string label returns $TYPE$
             return Load$NAME$Handle( ht, StringHash(subject), StringHash(label))
         endfunction
    //! endtextmacro
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Player","player")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Widget","widget")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Destructable","destructable")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Item","item")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Unit","unit")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Ability","ability")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Timer","timer")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Trigger","trigger")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("TriggerCondition","triggercondition")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("TriggerAction","triggeraction")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("TriggerEvent","event")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Force","force")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Group","group")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Location","location")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Rect","rect")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("BooleanExpr","boolexpr")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Sound","sound")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Effect","effect")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("UnitPool","unitpool")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("ItemPool","itempool")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Quest","quest")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("QuestItem","questitem")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("DefeatCondition","defeatcondition")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("TimerDialog","timerdialog")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Leaderboard","leaderboard")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Multiboard","multiboard")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("MultiboardItem","multiboarditem")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Trackable","trackable")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Dialog","dialog")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Button","button")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("TextTag","texttag")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Lightning","lightning")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Image","image")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Ubersplat","ubersplat")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Region","region")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("FogState","fogstate")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("FogModifier","fogmodifier")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Hashtable","hashtable")

    function CS_KillTrigger takes trigger t returns nothing
    // let em leak
    endfunction

    // too bad the Handle vars' old functionality forces me to make these things
    // inline-unfriendly
    function AttachObject takes agent subject,  string label, agent value returns nothing
        call SetTableObject( GetAttachmentTable(subject) , label, value)
    endfunction




    function GetAttachedHandle takes agent subject, string label returns agent
        debug call BJDebugMsg("[debug] What the heck? Why would you call HandleHandle I guess this was caused by a search and replace mistake")
        return null
    endfunction

    // these are inline friendly, ok, maybe they aren't because jasshelper does not recognize
    // GetHandleId as non-state changing. But they will be once I fix jasshelper...



    // got bored so I now use a textmacro...
    //! textmacro FAUX_HANDLE_VARS_GetAttachedHandle takes NAME, TYPE
         function Attach$NAME$ takes agent subject,  string label, $TYPE$ value returns nothing
             call SetTable$NAME$( GetAttachmentTable(subject), label, value)
         endfunction

         function GetAttached$NAME$ takes agent subject, string label returns $TYPE$
             return GetTable$NAME$( GetAttachmentTable(subject), label)
         endfunction
    //! endtextmacro
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Player","player")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Widget","widget")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Destructable","destructable")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Item","item")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Unit","unit")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Ability","ability")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Timer","timer")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Trigger","trigger")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("TriggerCondition","triggercondition")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("TriggerAction","triggeraction")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("TriggerEvent","event")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Force","force")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Group","group")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Location","location")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Rect","rect")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("BooleanExpr","boolexpr")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Sound","sound")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Effect","effect")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("UnitPool","unitpool")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("ItemPool","itempool")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Quest","quest")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("QuestItem","questitem")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("DefeatCondition","defeatcondition")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("TimerDialog","timerdialog")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Leaderboard","leaderboard")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Multiboard","multiboard")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("MultiboardItem","multiboarditem")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Trackable","trackable")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Dialog","dialog")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Button","button")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("TextTag","texttag")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Lightning","lightning")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Image","image")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Ubersplat","ubersplat")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Region","region")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("FogState","fogstate")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("FogModifier","fogmodifier")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Hashtable","hashtable")
 
 
    private function init takes nothing returns nothing
        set ht=InitHashtable()
    endfunction

endlibrary
 

Attachments

  • Faux CSCache With Everything.w3x
    51.1 KB · Views: 32
Last edited:
Level 1
Joined
Dec 21, 2016
Messages
7
W
Rather than trying to catch all the little things myself since you're only using CSCache you should be able to just replace it with Vexorian's faux-CSCache version for 1.24+. You will need the JASSNewGen pack to use this (just download the package, put in your wc3 directory, and use its new WE to open/save maps instead of the vanilla WE). Open my attached map and copy all 3 triggers over to yours. Delete the previous version of CSCache from your Custom Script section.

Finding a Vanilla WE solution to this will probably be difficult. And honestly Dr Super Good is right that you should really move to a different system that hasn't been outdated and deprecated.

Here's the full code of all 3 libraries required for CSCache to run properly in 1.24+:
JASS:
library CSData initializer init

    //****************************************************************************************************
    // CSData 16.0
    // ¯¯¯¯¯¯¯¯¯¯¯
    // CSDatas are like UserData in units and items, they now use a hashtable instead of big arrays,
    // should be faster and safer
    //
    // Notice that for public spells or systems to be distributed you should only use these
    // for private objects (those who the mapper would never have access to) If you are making something
    // for your map you can use them wherever you want.
    //
    // Best to be used in conjunction to CSArrays so you just specify an array id for a handle.
    //
    // DO NOT USE THIS ON THESE HANDLE TYPES: -lightning, -ubersplat, -image, -texttag,
    //                                        -any 'argument' handle (like playerstate, damagetype, etc)
    //
    //****************************************************************************************************

    //====================================================================================================
    globals
        private constant integer MAX_HANDLE_ID_COUNT = 408000
        // values lower than 8191: very fast, but very unsafe.
        // values bigger than 8191: not that fast, the bigger the number is the slower the function gets
        // Most maps don't really need a value bigger than 50000 here, but if you are unsure, leave it
        // as the rather inflated value of 408000
    endglobals

    //=================================================================================================
    // a.k.a H2I, changed name to CS_H2I to prevent conflicts with other systems, it then stayed that
    // instead of changing to a private or public function since many outside spells use it.
    //
    function CS_H2I takes handle h returns integer //For compat
        return GetHandleId(h)                      //
    endfunction                                    //

    //==================================================================================================
    globals
        private hashtable ht
    endglobals

    //It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
    function SetCSData takes handle h, integer v returns nothing
        call SaveInteger(ht , 0, GetHandleId(h) , v)
    endfunction

    function GetCSData takes handle h returns integer
        return LoadInteger( ht, 0, GetHandleId(h) )
    endfunction

    private function init takes nothing returns nothing
        set ht = InitHashtable()
    endfunction

endlibrary
JASS:
library CSSafeCache initializer init requires CSData
    //*************************************************************************************************
    //*                                                                                               *
    //* CSSafeCache 15.3                       http://wc3campaigns.net/vexorian                       *
    //* ¯¯¯¯¯¯¯                                                                                       *
    //*    CSCache is evil now, use just CSSafeCache when possible as it now contains all functions   *
    //* that do not use I2H or similar functions that are considered to cause unstability in the map  *
    //*                                                                                               *
    //*    CSCache has been deprecated, and is now unusuable since it can't be ported to patch 1.23b  *
    //* Migration from CSCache to CSSafeCache+structs should be easy                                  *
    //*                                                                                               *
    //* However, for old compatibility, CSSafeCache needs to stick to using gamecache, which is not   *
    //* as good as hashtable (it is slower and may be vulnerable to Sync attacks) I recommend         *
    //* switching from CSSafeCache to hashtable or Table                                              *
    //*                                                                                               *
    //*************************************************************************************************

    //=================================================================================================
    // CSSafeCache globals:
    //
    globals

        gamecache     cs_cache   = null
        integer array cs_array1
        integer array cs_array3
        integer array cs_array2
        private integer array cs_freeindexes
        private integer array cs_pairx
        private integer array cs_pairy
        private integer array cs_freepairs

    endglobals

    private function init takes nothing returns nothing
    // CSSafeCache initializer :
        call FlushGameCache(InitGameCache("cscache"))
        set cs_cache=InitGameCache("cscache")
        call ExecuteFunc("InitArrayIndexes")
        call ExecuteFunc("Pool_SetupCharMap")
    endfunction


    //=================================================================================================================
    // CS Pairs
    // ¯¯¯¯¯¯¯¯
    // This is a sub system to assist csarrays, you can use them but CSArrays of size 2 have the same functionality
    // although cspairs are be faster their space is limited and will start using gamecache if abused
    //
    function NewPair takes integer x, integer y returns integer
     local integer i
    
       if (cs_freepairs[0]==0) then
           set cs_freepairs[8190]=cs_freepairs[8190]+1
           set i= cs_freepairs[8190]
       else
           set i= cs_freepairs[cs_freepairs[0]]
           set cs_freepairs[0]=cs_freepairs[0]-1
       endif
       if (i>=8189) then //because we can only recycle up to 8189 (free pairs array uses indexes 0 and 8190 for other purposes)
           call StoreInteger(cs_cache,"pairx",I2S(i),x)
           call StoreInteger(cs_cache,"pairy",I2S(i),y)
       else
           set cs_pairx[i]=x
           set cs_pairy[i]=y

       endif
     return i
    endfunction

    function DestroyPair takes integer id returns nothing
        if (id>=8189) then
            call FlushStoredInteger(cs_cache,"pairx",I2S(id))
            call FlushStoredInteger(cs_cache,"pairy",I2S(id))
        else
            set cs_freepairs[0]=cs_freepairs[0]+1
            set cs_freepairs[cs_freepairs[0]] = id
        endif
    endfunction

    function SetPairXY takes integer id, integer x, integer y returns nothing
        if (id>=8189) then
            call StoreInteger(cs_cache,"pairx",I2S(id),x)
            call StoreInteger(cs_cache,"pairy",I2S(id),y)

        else
            set cs_pairx[id]=x
            set cs_pairy[id]=y
        endif
    endfunction

    function SetPairX takes integer id, integer x returns nothing
        if (id>=8189) then
            call StoreInteger(cs_cache,"pairx",I2S(id),x)
        else
            set cs_pairx[id]=x
        endif
    endfunction

    function SetPairY takes integer id, integer y returns nothing
        if (id>=8189) then
            call StoreInteger(cs_cache,"pairy",I2S(id),y)
        else
            set cs_pairy[id]=y
        endif
    endfunction

    function GetPairX takes integer id returns integer
        if (id>=8189) then
            return GetStoredInteger(cs_cache,"pairy",I2S(id))
        endif
     return cs_pairx[id]
    endfunction
    function GetPairY takes integer id returns integer
        if (id>=8189) then
            return GetStoredInteger(cs_cache,"pairx",I2S(id))
        endif
     return cs_pairy[id]
    endfunction





    //=================================================================================================================
    // CS Dynamic Arrays
    // ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    // Thanks: Pipedream, Peppar
    //
    // We can now create arrays in game! , also pass them as arguments or return values!
    // a 1 length array is a pointer!
    //
    function Array_TryFree takes nothing returns nothing
     local integer i
     local integer N=cs_array1[0]
     local integer k
     local boolean cleaned=false

     local integer loc

     local integer q
     local integer r
      


            set i=cs_array1[146]
            if (i>144) then
                call TimerStart(GetExpiredTimer(),60.,false,function Array_TryFree)
                return
            endif

            set loc=cs_freeindexes[i]
            set q=0
            loop
                exitwhen (loc==0)
           // we could just have used:
                    //set k=GetPairX(loc)
                    //set r=GetPairY(loc) But it is slower than direct usage:

                if (loc>=8192) then
               set k=GetStoredInteger(cs_cache,"pairx",I2S(loc))
            set r=GetStoredInteger(cs_cache,"pairy",I2S(loc))
           else
               set k=cs_pairx[loc]
            set r=cs_pairy[loc]
           endif

                if (k+i-1==N) then


                    //we found one we can remove from the list

                    set cleaned=true
                    //decrement N
                  
                    set N=k-2

                    //Remove from the list:
                    if (q==null) then
                        //That was the first, update the array as well
                        set cs_freeindexes[i]=r
                    else
                        //Update the next of the previous one
                        //We could use : call SetPairY(q,,r) but it is slower
               if (q>=8189) then
                   call StoreInteger(cs_cache,"pairy",I2S(q),r)
                        else
                set cs_pairy[q]=r
               endif


                    endif
                    if (r==null) then
                        //This was the last one, update it in the array as well
                        set cs_freeindexes[i+4096]=q
                    endif
                  
                    call DestroyPair(loc)
                    set loc=q
                endif
                set q=loc
                set loc=r
            endloop
          
            if (cleaned) then
                set cs_array1[0]=N
                set cs_array1[146]=1
            else
                set cs_array1[146]=cs_array1[i+1]
            endif
     call TimerStart(GetExpiredTimer(),0.2,false,function Array_TryFree)

    endfunction

    function InitArrayIndexes2 takes nothing returns nothing
     local integer i=0
        loop
            exitwhen (i==8191)
            set cs_pairx[i]=777
            set cs_pairy[i]=777
            set i=i+1
        endloop
    
    endfunction


    function InitArrayIndexes takes nothing returns nothing
     local integer i=0
     local integer a=1
     local integer b=1
     local integer c


        //By placing 777 there instead of 0 it is easier to recognize non correctly initialized bugs
        loop
            exitwhen (i== 8191)
            set cs_array1[i]=777
            set cs_array2[i]=777
            set cs_array3[i]=777
            //set cs_pairx[i]=777
            //set cs_pairy[i]=777
            set i=i+1
        endloop
        call ExecuteFunc("InitArrayIndexes2")


        set cs_freeindexes[0]=0 //The stack for the table indexes.
        set cs_freepairs[0]=0
        set cs_freepairs[8190]=0
        set i=1
        loop
            set c=a+b
            set a=b
            set b=c
            exitwhen (b>144) //max size is 144
            set cs_freeindexes[b]=0 //the first of the list
            set cs_freeindexes[b+4096]=0 //the last of the list
            loop
                exitwhen (i>b)
                set cs_array1[i]=b
                set i=i+1
            endloop
          
        endloop
        set cs_array1[i]=b //i is 145
        set cs_array1[146]=1
        set cs_array1[147]=101 //initial table index is 101
        set cs_array1[0]=147
        //index 0: Last used index
        // 1 to 145 : Fibonacci sequence
        // 146 : last check
        // 147 : Table indexes check
      
        //This has a good chance to compress the thing when necesary
        call TimerStart(CreateTimer(),60.,false,function Array_TryFree)

    endfunction

    //=============================================================================================
    // Create an array of size, max size is 144, if doinit is true it will put a bunch of zeros
    // in the indexes
    //
    function NewArray takes integer size, boolean doinit returns integer
     local integer i
     local integer rsize=cs_array1[size]
     local integer loc

        set loc=cs_freeindexes[rsize]
        if (loc!=0) then

            set cs_freeindexes[rsize]= GetPairY(loc)
            if (cs_freeindexes[rsize]==0) then
                set cs_freeindexes[4096+rsize]=0
            endif
            set i=GetPairX(loc)
            call DestroyPair(loc)

            if (i==0) then
                //this code was probably a good idea when we used locations for the free indexes list, now we use pairs which should not ever
                //do this unless someone modiffied the pair array incorrectly
          
                call BJDebugMsg("Caster System: Unexpected error (5): corrupt stack, attempt to recover "+I2S(rsize))
                // recovering involves forgetting about the stack which got corrupted and start again from zero, it will leak
                // and probably get slow due to usage of gamecache but it is better than the problems that a corrupt stack might cause
                set cs_freeindexes[rsize]=0
                set cs_freeindexes[4096+rsize]=0
              
              
                return NewArray(size,doinit)
            endif

        else
          
            //sz  i   i+1   i+2
            //[ ][   ][    ][   ]
      
            set i=cs_array1[0]+2
            set cs_array1[0]=i+rsize-1

        endif

        //It used to store size in the index equal to the array's id
        // but that required the get/set functions to increment 1 in every index
        // calculation. Instead, making size the previous index to the array works
        if (i<=8191) then
            set cs_array1[i-1]=size
        elseif (i<=16382) then
            set cs_array2[i-8192]=size
        else
            call StoreInteger(cs_cache,I2S(-i),"size",size)
        endif

      
        if (not doinit) then
            return i
        endif
      
        //    3
        //[i][i+1][i+2]

        set size=i+size-1
        if (size>=16382) then
            set size=16381
        endif
        loop
            exitwhen (size<i) or (size<8191)
            set cs_array2[size-8191]=0
            set size=size-1
        endloop
        loop
            exitwhen (size<i)
            set cs_array1[size]=0
            set size=size-1
        endloop
      
        //call DisplayTextToPlayer(GetLocalPlayer(),0,0,I2S(i))

     return i
    endfunction


    //===============================================================================================================
    // Remember to destroy arrays when you no longer need them, else new arrays will get slower after a bunch of
    // arrays are active
    //
    function DestroyArray takes integer id returns nothing
     local integer L
     local integer loc
    // local string k=I2S(-id)
     local integer lstpace

        if (id<=8191) then
            set L=cs_array1[cs_array1[id-1]]
        elseif (id<=16382) then
            set L=cs_array1[cs_array2[id-8191]]
        else
            set L=cs_array1[GetStoredInteger(cs_cache,I2S(-id),"size")]
            //No way you are gonna call DestroyArray without calling
           //NewArray first, so we can use the gamecache variable directly instead

        endif
        set lstpace=id+L-1
       call FlushStoredMission(cs_cache,I2S(-id))
    
       if (lstpace>16382) then
      
           if (lstpace==cs_array1[0]) then
               //We just destroyed the array that happens to be at the end of the heap.
               //Just get it back
               set cs_array1[0]=id-2
               set cs_array1[146]=1
           else
               //Add to the last
               set loc=cs_freeindexes[L+4096]
               if (loc==0) then
                   set loc=NewPair(id,0)
                   set cs_freeindexes[L]=loc
                   set cs_freeindexes[L+4096]=loc
               else
                   set cs_freeindexes[L+4096]=  NewPair(id,0)
                   //we could just use: call SetPairY(loc, cs_freeindexes[L+4096] ) //but that's slower
                   if (loc>=8189) then
                  call StoreInteger(cs_cache,"pairy",I2S(loc), cs_freeindexes[L+4096] )
                   else
                       set cs_pairy[loc]=cs_freeindexes[L+4096]              
                   endif

               endif


           endif

       elseif (lstpace==cs_array1[0]) then
               //We just destroyed the array that happens to be at the end of the heap.
               //Just get it back
               set cs_array1[0]=id-2
               set cs_array1[146]=1
       else
           set loc=cs_freeindexes[L]
           set cs_freeindexes[L]=NewPair(id,loc)

           if (loc==0) then
               set cs_freeindexes[L+4096]=cs_freeindexes[L]
           endif

       endif

    endfunction

    //================================================================================================================
    // Int Set/Get array usage prototypes.
    //
    // These are the actual functions, the rest are just the result of copy paste, if these functions are updated
    // the other ones should be updated as well (They are just return bugged variations)
    //
    function SetArrayInt takes integer id, integer index, integer val returns nothing
        set index=id+index
        if (index<8191) then
            set cs_array1[index]=val
        elseif (index<16382) then   
            set cs_array2[index-8191]=val
        else
            call StoreInteger(cs_cache,I2S(-id),I2S(index),val)
        endif
    endfunction
    function GetArrayInt takes integer id, integer index returns integer
        set index=id+index
        if (index<8191) then
            return cs_array1[index]
        elseif (index<16382) then   
            return cs_array2[index-8191]
        endif
     return GetStoredInteger(cs_cache,I2S(-id),I2S(index))
    endfunction


    //================================================================================================================
    // String Set/Get array
    //
    // Due String related return bug issues, these are forced to use gamecache
    //
    function SetArrayString takes integer id, integer index, string val returns nothing
        call StoreString(cs_cache,I2S(-id),I2S(index),val)
    endfunction
    function GetArrayString takes integer id, integer index returns string
     return GetStoredString(cs_cache,I2S(-id),I2S(index))
    endfunction

    //(Boolean is not needed)

    //==========================================================================================================================
    // Returns the size of an array (the specified by player one, not the actual size of it) should be useful.
    //
    function GetArraySize takes integer id returns integer
        if (id<=8191) then
            return cs_array1[id-1]
        elseif (id<=16382) then
            return cs_array2[id-8192]
        endif
     return GetStoredInteger(cs_cache,I2S(-id),"size")
    endfunction


    //===========================================================================================================================
    // Returns an array that is an exact copy of the given array
    //
    function CloneArray takes integer id returns integer
     local integer sz
     local integer i
     local integer sz2
     local integer x
     local integer y

        if (id<=8191) then
            set sz=cs_array1[id-1]
        elseif (id<=16382) then
            set sz=cs_array2[id-8192]
        else
            set sz=GetStoredInteger(cs_cache,I2S(-id),"size")
            //No way you are gonna call DestroyArray without calling
           //NewArray first, so we can use the gamecache variable directly instead

        endif

        set i=NewArray(sz,false)
      
        set sz2=i+sz-1
        set sz=id+sz-1
        set x=i
        set y=id
        loop
            exitwhen ((y>sz) or (y>=8191) or (x>=8191))
            set cs_array1[x]=cs_array1[y]
            set y=y+1
            set x=x+1
        endloop
        loop
            exitwhen ((y>sz) or (y>=8191) or (x>=16382))
            set cs_array2[x-8191]=cs_array1[y]
            set y=y+1
            set x=x+1
        endloop
        loop
            exitwhen ((y>sz) or (y>=8191))
            call StoreInteger(cs_cache,I2S(-i),I2S(x-i),cs_array1[y])
            set x=y+1
            set y=y+1
        endloop
        //...
        loop
            exitwhen ((y>sz) or (y>=16382) or (x>=8191))
            set cs_array1[x]=cs_array2[y-8191]
            set y=y+1
            set x=x+1
        endloop
        loop
            exitwhen ((y>sz) or (y>=16382) or (x>=16382))
            set cs_array2[x-8191]=cs_array2[y-8191]
            set y=y+1
            set x=x+1
        endloop
        loop
            exitwhen ((y>sz) or (y>=16382))
            call StoreInteger(cs_cache,I2S(-i),I2S(x-i),cs_array2[y-8191])
            set y=y+1
            set x=x+1
        endloop
        //...
        loop
            exitwhen ((y>sz) or (x>=8191))
            set cs_array1[x]=GetStoredInteger(cs_cache,I2S(-id),I2S(y-id))
            set y=y+1
            set x=x+1
        endloop
        loop
            exitwhen ((y>sz) or (x>=16382))
            set cs_array2[x-8191]=GetStoredInteger(cs_cache,I2S(-id),I2S(y-id))
            set y=y+1
            set x=x+1
        endloop
        loop
            exitwhen (y>sz)
            call StoreInteger(cs_cache,I2S(-i),I2S(x-i),GetStoredInteger(cs_cache,I2S(-id),I2S(y-id)))
            set y=y+1
            set x=x+1
        endloop
      
     return i
    endfunction

    //==================================================================================================
    // Attachable vars : Attacheable variables are what most other people call Handle Variables, they
    // allow to relate data with any handle, using a label, and its value, the stuff auto flushes if
    // the value is 0, false, "", or null .
    //
    // Differences between Attacheable variables and "Local Handle Variables" :
    // - The names of the functions
    // - The name of the function group does not cause confusion, it is difficult to say:
    //   "you should set local handle variables to null at the end of a function" since
    //   it sounds as if you were talking about the "Local Handle Variables"
    // - And can work together with Tables.
    //
    // Gamecache stuff are NOT Case Sensitive, don't ever use "" for label (Crashes game!)
    //
    //
    // Although locations and dynamic arrays are faster than gamecache, gamecache still keeps the flexibility
    // Best thing to do in my opinion is to combine these options. By combining you can acquire gamecache
    // flexibility and arrays/locs speed to solve a problem
    //

    //============================================================================================================
    // For integers
    //
    function AttachInt takes handle h, string label, integer x returns nothing
        if x==0 then
            call FlushStoredInteger(cs_cache,I2S(GetHandleId(h)),label)
        else
            call StoreInteger(cs_cache,I2S(GetHandleId(h)),label,x)
        endif
    endfunction
    function GetAttachedInt takes handle h, string label returns integer
        return GetStoredInteger(cs_cache, I2S(GetHandleId(h)), label)
    endfunction

    //=============================================================================================================
    function AttachReal takes handle h, string label, real x returns nothing
         if x==0 then
            call FlushStoredReal(cs_cache,I2S(GetHandleId(h)),label)
        else
            call StoreReal(cs_cache,I2S(GetHandleId(h)),label,x)
        endif
    endfunction
    function GetAttachedReal takes handle h, string label returns real
        return GetStoredReal(cs_cache,I2S(GetHandleId(h)),label)
    endfunction

    //=============================================================================================================
    function AttachBoolean takes handle h, string label, boolean x returns nothing
        if not x then
            call FlushStoredBoolean(cs_cache,I2S(GetHandleId(h)),label)
        else
            call StoreBoolean(cs_cache,I2S(GetHandleId(h)),label,x)
        endif
    endfunction
    function GetAttachedBoolean takes handle h, string label returns boolean
        return GetStoredBoolean(cs_cache,I2S(GetHandleId(h)),label)
    endfunction

    //=============================================================================================================
    function AttachString takes handle h, string label, string x returns nothing
        if ((x=="") or (x==null)) then
            call FlushStoredString(cs_cache,I2S(GetHandleId(h)),label)
        else
            call StoreString(cs_cache,I2S(GetHandleId(h)),label,x)
        endif
    endfunction
    function GetAttachedString takes handle h, string label returns string
        return GetStoredString(cs_cache,I2S(GetHandleId(h)),label)
    endfunction

    //============================================================================================================
    function CleanAttachedVars takes handle h returns nothing
        call FlushStoredMission(cs_cache,I2S(GetHandleId(h)))
    endfunction

    //============================================================================================================
    // Left for compat
    function CleanAttachedVars_NoSets takes handle h returns nothing
        call FlushStoredMission(cs_cache,I2S(GetHandleId(h)))
    endfunction


    //=============================================================================================
    // Tables
    //
    // Tables are lame, the real name would be hash tables, they are just abbreviated usage
    // of gamecache natives with the addition that you can also Copy the values of a table to
    // another one, but don't expect it to be automatic, it must use a FieldData object to know
    // which fields and of wich types to copy, Copying a table to another, with a lot of Fields,
    // should surelly be lag friendly.
    //
    // The other thing about tables is that I can say that the Attached variables of a handle work
    // inside a table and GetAttachmentTable which is just return bug and I2S , works to allow you
    // to manipulate a handle's attached variables through a table.
    //
    // NewTable and DestroyTable were created to allow to create tables in the fly, but you can
    // simply use strings for tables, but place the table names should be between "("")" for example
    // "(mytable)" to avoid conflicts with other caster system stuff.
    //
    function NewTableIndex takes nothing returns integer
     local integer loc=cs_freeindexes[0]
     local integer i
        if (loc!=0) then
            set i=GetPairX(loc)
            set cs_freeindexes[0]=GetPairY(loc)
            call DestroyPair(loc)

            return i
        endif
        set i=cs_array1[147]+1
        set cs_array1[147]=i

     return i
    endfunction
    function NewTable takes nothing returns string
     local integer loc=cs_freeindexes[0]
     local integer i
        if (loc!=0) then
            set i=GetPairX(loc)
            set cs_freeindexes[0]=GetPairY(loc)
            call DestroyPair(loc)

            return I2S(i)
        endif
        set i=cs_array1[147]+1
        set cs_array1[147]=i

     return I2S(i)
    endfunction
    function GetAttachmentTable takes handle h returns string
        return I2S(GetHandleId(h))
    endfunction

    //============================================================================================================
    function DestroyTable takes string table returns nothing
     local integer i=S2I(table)
     local integer n
         call FlushStoredMission(cs_cache,table)
         if ((i>100) and (i<1000000)) then //All right, more than 1000000 tables is just wrong.
             if (i==cs_array1[147]) then
                 set cs_array1[147]=cs_array1[147]-1
             else
                 set cs_freeindexes[0]=  NewPair(i,cs_freeindexes[0])
             endif
         endif

    endfunction

    //============================================================================================================
    function ClearTable takes string table returns nothing
         call FlushStoredMission(cs_cache,table)
    endfunction


    //============================================================================================================
    function SetTableInt takes string table, string field, integer val returns nothing
        if (val==0) then
            call FlushStoredInteger(cs_cache,table,field)
        else
            call StoreInteger(cs_cache,table,field,val)
        endif
    endfunction
    function GetTableInt takes string table, string field returns integer
        return GetStoredInteger(cs_cache,table,field)
    endfunction

    //============================================================================================================
    function SetTableReal takes string table, string field, real val returns nothing
        if (val==0) then
            call FlushStoredReal(cs_cache,table,field)
        else
            call StoreReal(cs_cache,table,field,val)
        endif
    endfunction
    function GetTableReal takes string table, string field returns real
        return GetStoredReal(cs_cache,table,field)
    endfunction

    //============================================================================================================
    function SetTableBoolean takes string table, string field, boolean val returns nothing
        if (not(val)) then
            call FlushStoredBoolean(cs_cache,table,field)
        else
            call StoreBoolean(cs_cache,table,field,val)
        endif
    endfunction
    function GetTableBoolean takes string table, string field returns boolean
        return GetStoredBoolean(cs_cache,table,field)
    endfunction

    //============================================================================================================
    function SetTableString takes string table, string field, string val returns nothing
        if (val=="") or (val==null) then
            call FlushStoredString(cs_cache,table,field)
        else
            call StoreString(cs_cache,table,field,val)
        endif
    endfunction
    function GetTableString takes string table, string field returns string
        return GetStoredString(cs_cache,table,field)
    endfunction

    //============================================================================================================
    // Returns true if the fiel contains a value different from 0, false,  null, or "" (depending on the type)
    // it is worthless to use this with boolean, since it would be the same as reading the boolean value
    //
    function HaveSetField takes string table, string field, integer fieldType returns boolean
        if (fieldType == bj_GAMECACHE_BOOLEAN) then
            return HaveStoredBoolean(cs_cache,table,field)
        elseif (fieldType == bj_GAMECACHE_INTEGER) then
            return HaveStoredInteger(cs_cache,table,field)
        elseif (fieldType == bj_GAMECACHE_REAL) then
            return HaveStoredReal(cs_cache,table,field)
        elseif (fieldType == bj_GAMECACHE_STRING) then
            return HaveStoredString(cs_cache,table,field)
        endif
     return false
    endfunction

    //============================================================================================================
    // Allows to copy a table to another one, but it needs a FieldData object to know which fields of which type
    // it is supposed to copy.
    //
    function CopyTable takes integer FieldData, string sourceTable, string destTable returns nothing
     local integer i=1
     local string k=I2S(FieldData)
     local string k2
     local string k3
     local integer n=GetStoredInteger(cs_cache,k,"N")
     local integer t
        loop
            exitwhen (i>n)
            set k2=I2S(i)
            set t=GetStoredInteger(cs_cache,k,k2)
            set k3=GetStoredString(cs_cache,k,k2)
            if (t==bj_GAMECACHE_BOOLEAN) then
                if (HaveStoredBoolean(cs_cache,sourceTable,k3)) then
                    call StoreBoolean(cs_cache,destTable,k3,GetStoredBoolean(cs_cache,sourceTable,k3))
                else
                    call FlushStoredBoolean(cs_cache,destTable,k3)
                endif
            elseif (t==bj_GAMECACHE_INTEGER) then
                if (HaveStoredInteger(cs_cache,sourceTable,k3)) then
                    call StoreInteger(cs_cache,destTable,k3,GetStoredInteger(cs_cache,sourceTable,k3))
                else
                    call FlushStoredInteger(cs_cache,destTable,k3)
                endif
            elseif (t==bj_GAMECACHE_REAL) then
                if (HaveStoredReal(cs_cache,sourceTable,k3)) then
                    call StoreReal(cs_cache,destTable,k3,GetStoredReal(cs_cache,sourceTable,k3))
                else
                    call FlushStoredReal(cs_cache,destTable,k3)
                endif
            elseif (t==bj_GAMECACHE_STRING) then
                if (HaveStoredString(cs_cache,sourceTable,k3)) then
                    call StoreString(cs_cache,destTable,k3,GetStoredString(cs_cache,sourceTable,k3))
                else
                    call FlushStoredString(cs_cache,destTable,k3)
                endif
            endif
            set i=i+1
        endloop


    endfunction

    //=============================================================================================
    // FieldData inherits from Table, was just designed to be used by CopyTable.
    //
    function FieldData_Create takes nothing returns integer
        return NewTableIndex()
    endfunction


    //============================================================================================================
    // valueType uses the same integer variables from blizzard.j :
    // bj_GAMECACHE_BOOLEAN, bj_GAMECACHE_INTEGER, bj_GAMECACHE_REAL and bj_GAMECACHE_STRING
    //
    function FieldData_AddField takes integer fielddata, string field, integer valueType returns nothing
     local string k=I2S(fielddata)
     local integer n=GetStoredInteger(cs_cache,k,"N")+1
     local string k2=I2S(n)

        call StoreString(cs_cache,k,k2,field)
        call StoreInteger(cs_cache,k,k2,valueType)
        call StoreInteger(cs_cache,k,"N",n)

    endfunction

    //=============================================================================================
    // Destroys Field Data
    function FieldData_Destroy takes integer fielddata returns nothing
        call DestroyTable(I2S(fielddata))
    endfunction

    //=============================================================================================
    // Pools
    //
    // A better name for pools would be sets, but by the time I made them I couldn't think of that
    // name, besides the word set is already a JASS syntax word so it would have been problematic.
    //
    // Another naming failure is that values of a pool are called "items" but that conflicts with
    // the word item that points to wc3 items, Pools can only store integer values, but if you want
    // you can go and use the return bug on them.
    //

    function CreatePool takes nothing returns integer
     local integer i=NewArray(34,false)
        call SetArrayInt(i,0,0)
     return i
    endfunction

    function ClearPool takes integer poolid returns nothing
        call SetArrayInt(poolid,0,0) //[0:integer:n]
        call FlushStoredMission(cs_cache,I2S(-poolid))
    endfunction

    function DestroyPool takes integer poolid returns nothing
        call DestroyArray(poolid)
    endfunction


    function PoolAddItem takes integer poolid, integer value returns nothing
     local integer n
     local string k=I2S(-poolid)
     local string vk="#"+I2S(value)
        if not HaveStoredInteger(cs_cache,k,vk) then
            set n=GetArrayInt(poolid,0)+1 //[0:integer:N]
            call StoreInteger(cs_cache,k,vk,n)
            if (n>33) then
                call StoreInteger(cs_cache,k,I2S(n),value)
            else
                call SetArrayInt(poolid,n,value)
            endif
            call SetArrayInt(poolid,0,n) //[0:integer:N]
        endif
    endfunction



    function PoolRemoveItem takes integer poolid, integer value returns nothing
     local string k=I2S(-poolid)
     local string vk="#"+I2S(value)

     local integer p=GetStoredInteger(cs_cache,k,vk)
     local integer n

        if (p!=0) then
            set n=GetArrayInt(poolid,0) //[0:integer:N]
            call FlushStoredInteger( cs_cache, k, vk)
          
            if (n>p) then
          
                if (n>33) then
                    set vk=I2S(n)
                    set value=GetStoredInteger(cs_cache,k,vk)
                    call FlushStoredInteger(cs_cache,k,vk)
                else
                    set value=GetArrayInt(poolid,n)
                endif
                call StoreInteger(cs_cache,k,"#"+I2S(value),p)
                if (p>33) then
                    call StoreInteger(cs_cache,k,I2S(p),value)
                else
                    call SetArrayInt(poolid,p,value)
                endif
            elseif (p>33) then
                call FlushStoredInteger(cs_cache,k,I2S(p))
            endif
            call SetArrayInt( poolid,0,n - 1) //[0:integer:N]
        endif
    endfunction

    //===================================================================================
    function PoolGetItem takes integer poolid, integer itemn returns integer
        if (itemn>33) then
            return GetStoredInteger( cs_cache, I2S(-poolid), I2S(itemn))
        endif
     return GetArrayInt(poolid,itemn)
    endfunction

    //===================================================================================
    function CountItemsInPool takes integer poolid returns integer
     return GetArrayInt(poolid,0) //[0:integer:N]
    endfunction


    //===================================================================================
    // Removed : GetEnumPoolItem , ForPool and ForPool2 they are much worse than just
    // using CountItemsInPool and PoolGetItem to iterate the pool
    //

    //===================================================================================
    function GetFirstOfPool takes integer poolid returns integer
        return GetArrayInt(poolid,1) //[1 is just the first of the pool]
    endfunction


    //===================================================================================
    function PoolPickRandomItem takes integer poolid returns integer
     local integer p=GetRandomInt(  1, GetArrayInt(poolid,0) )
        if (p>33) then
            return GetStoredInteger( cs_cache, I2S(-poolid), I2S(p))
        endif
     return GetArrayInt(poolid,p)
    endfunction

    //===================================================================================
    function GetItemPositionInPool takes integer poolid, integer it returns integer
        return GetStoredInteger( cs_cache, I2S(-poolid), "#"+I2S(it))
    endfunction

    //===================================================================================
    function IsItemInPool takes integer poolid, integer it returns boolean
        return(HaveStoredInteger( cs_cache, I2S(-poolid), "#"+I2S(it)) )
    endfunction

    //===================================================================================
    // This had to be optimized for speed, if it was just a loop using the above functions
    // that would have been too slow to be worth keeping. That's a bad thing about JASS
    // it is such an slow language that code reusability always has the cost of speed
    //
    function PoolAddPool takes integer sourcepoolid, integer destpoolid returns nothing
     local integer a=1
     local integer n=GetArrayInt( sourcepoolid,0) //[0:integer:N]
     local integer dn=GetArrayInt( destpoolid,0) //[0:integer:N]
     local string sk=I2S(-sourcepoolid)

     local string k=I2S(-destpoolid)
     local integer v
     local string vk

        loop
            exitwhen (a>n)
            if (a>33) then
                set v=GetStoredInteger(cs_cache,sk,I2S(a))
            else
                set v=GetArrayInt(sourcepoolid,a)
            endif
            set vk="#"+I2S(v)
            if not HaveStoredInteger(cs_cache,k,vk) then
                set dn=dn+1
                call StoreInteger(cs_cache,k,vk,dn)
                if (dn>33) then
                    call StoreInteger(cs_cache,k,I2S(dn),v)
                else
                    call SetArrayInt(destpoolid,dn,v)
                endif
            endif
            set a=a+1
        endloop
        call SetArrayInt(destpoolid,0,dn) //[0:integer:N]
    endfunction

    //=============================================================================================
    // Oh darn, After making PoolAddPool I don't feel like writting this one
    // All right I am at least make the get code
    //
    function PoolRemovePool takes integer sourcepoolid, integer destpoolid returns nothing
     local integer a=1
     local integer n=GetArrayInt( sourcepoolid,0) //[0:integer:N]
     local integer v
     local string sk=I2S(-sourcepoolid)
        loop
            exitwhen a>n
            if (a>33) then
                set v=GetStoredInteger(cs_cache,sk,I2S(a) )
            else
                set v=GetArrayInt(sourcepoolid,a)
            endif
            call PoolRemoveItem( destpoolid, v)
            set a=a+1
        endloop
    endfunction

    //===================================================================================
    // Adds a tokenized string to a pool,
    //  Example: PoolAddS(udg_p, "1;2;3;4") will add to the udg_p pool : 1,2,3 and 4
    //
    function PoolAddS takes integer poolid, string s returns nothing
     local integer i=0
     local integer st
     local string c
        set s=s+";"
        set st=0
        loop
            set c=SubString(s, i, i+1)
            exitwhen (c == "") or (c == null)
            if (c == ";") then
                call PoolAddItem( poolid, S2I(SubString(s, st, i)) )
                set st=i+1
            endif
            set i=i+1
        endloop
    endfunction

    //===================================================================================
    // Converts a tokenized string into a pool,
    //  Example: S2Pool( "1;2;3;4") will return a pool that has 1,2,3 and 4 inside
    //
    function S2Pool takes string s returns integer
     local integer spool= CreatePool()
        call PoolAddS(spool,s)
     return spool
    endfunction

    //===================================================================================
    // Does the opposite of S2Pool, debugging is a good use for this function.
    //
    function Pool2S takes integer P returns string
     local integer N=CountItemsInPool(P)
     local integer i
     local string s
        if (N>=1) then
            set s=I2S(PoolGetItem(P,1) )
            set i=2
        else
            return ""
        endif
        loop
            exitwhen (i>N)
            set s=s+";"+I2S(PoolGetItem(P,i))
            set i=i+1
        endloop
    return s
    endfunction

    //=============================================================================================================
    // Fixes a lame bug by blizzard related to the custom script section (The condition of the if statement might
    // actually be true.
    //
    function Pool_Percent takes nothing returns string
       if ("%"=="") then
           return "%%"
       endif
     return "%"
    endfunction

    function Pool_SetupCharMap takes nothing returns nothing
     local string cm=".................................!.#$"+Pool_Percent()+"&'()*+,-./0123456789:;<=>.@ABCDEFGHIJKLMNOPQRSTUVWXYZ[.]^_`abcdefghijklmnopqrstuvwxyz{|}~................................................................................................................................."
     local integer i=0
     local string c
        if HaveStoredInteger(cs_cache,"charmap_upper","A") then
            return
        endif
        loop
            set c=SubString(cm,i,i+1)
            exitwhen (c==null) or (c=="")
            if (c!=".") then
                if c==StringCase(c,true) then
                    call StoreInteger(cs_cache,"charmap_upper",c,i)
                else
                    call StoreInteger(cs_cache,"charmap_lower",c,i)
                endif
              
            endif
            set i=i+1
        endloop

    endfunction


    function Pool_Rawcode2Int takes string s returns integer
     local string c
     local integer i=0
     local integer r=0
        loop
            exitwhen i>3
            set c=SubString(s,i,i+1)
            set r=r*256
            if c==StringCase(c,true) then
                set r=r+GetStoredInteger(cs_cache,"charmap_upper",c)
            else
                set r=r+GetStoredInteger(cs_cache,"charmap_lower",c)
            endif
          
            set i=i+1
        endloop
     return r
    endfunction

    function PoolAddRawcodes_thread takes nothing returns nothing
    //Threaded because I don't want it to halt execution for no reason
    //
     local string s=bj_lastPlayedMusic
     local integer poolid=bj_groupEnumTypeId
     local string c
     local integer i=0
     local integer st=0
        set s=s+";"
        loop
            set c=SubString(s, i, i+1)
            exitwhen (c == "") or (c == null)
            if c == ";" then
                call PoolAddItem(poolid, Pool_Rawcode2Int(SubString(s,st,i) ))
                set st=i+1
            endif
            set i=i+1
        endloop

    endfunction

    //=====================================================================================================================
    // Adds a string of tokenized rawcodes to a pool
    //  Example: PoolAddRawcodes(udg_p,"A000;A001")  will add 'A000' and 'A001' to the pool
    //
    // (Saves some lines, but is not as good efficiency wise)
    //
    function PoolAddRawcodes takes integer poolid, string s returns nothing
     local string b=bj_lastPlayedMusic
        set bj_groupEnumTypeId=poolid
        set bj_lastPlayedMusic=s
        call ExecuteFunc("PoolAddRawcodes_thread")
        set bj_lastPlayedMusic=b
    endfunction

    //===================================================================================================================
    // Converts a tokenized string of rawcodes into a pool,
    //  Example: Rawcodes2Pool( "A000;A001;AHbz;S000") will return a pool that has 'A000,'A001','AHbx' and 'S000' inside
    //
    // (Saves some lines, but is not as good efficiency wise)
    //
    function Rawcodes2Pool takes string s returns integer
     local integer spool= CreatePool()
        call PoolAddRawcodes(spool,s)
     return spool
    endfunction

    //===================================================================================================================
    // A subproduct of the Pool's Rawcode support is that we can easily have this function so I am including it even if
    // it has nothing to do with data storage.
    //
    // takes "Aloc" and converts it into 'Aloc'
    // it is different to the Pool_Rawcode2Int function in that it is safe to use it when it is the first CSCache
    // function ever used. But it is a little slower (wc3mapoptimizer should make it as fast though)
    //
    function CS_Rawcode2Int takes string s returns integer
     local string c
     local integer i=0
     local integer r=0
        loop
            exitwhen i>3
            set c=SubString(s,i,i+1)
            set r=r*256
            if c==StringCase(c,true) then
                set r=r+GetStoredInteger(cs_cache,"charmap_upper",c)
            else
                set r=r+GetStoredInteger(cs_cache,"charmap_lower",c)
            endif
          
            set i=i+1
        endloop
     return r
    endfunction


endlibrary
JASS:
/*******************************************************************************************
 Faux CSCache AttachVars
----------------
    Do not use these functions for new stuff, it is just not the right thing to do...

    The intention of Faux CSCache  is to be a patch fix for a map's migration to
  patch 1.24 This library might not cover all uses of handle vars, some of them are just
  impossible with the new rules set in patches 1.23b and 1.24, but they should work for
  most of the cases....

*******************************************************************************************/

//==========================================================================================
library CSCache initializer init requires CSSafeCache

globals
    private hashtable ht
endglobals


    // too bad the Handle vars' old functionality forces me to make these things
    // inline-unfriendly
    function SetTableObject takes string subject,  string label, agent value returns nothing
        if(value==null) then
            call RemoveSavedHandle( ht, StringHash(subject), StringHash(label))
        else
            call SaveAgentHandle( ht, StringHash(subject), StringHash(label), value)
        endif
    endfunction




    function GetTableHandle takes string subject, string label returns agent
        debug call BJDebugMsg("[debug] What the heck? Why would you call HandleHandle I guess this was caused by a search and replace mistake")
        return null
    endfunction

    // these are inline friendly, ok, maybe they aren't because jasshelper does not recognize
    // GetHandleId as non-state changing. But they will be once I fix jasshelper...



    // got bored so I now use a textmacro...
    //! textmacro FAUX_HANDLE_VARS_GetTableHandle takes NAME, TYPE
         function SetTable$NAME$ takes string subject,  string label, $TYPE$ value returns nothing
             if(value==null) then
                call RemoveSavedHandle( ht, StringHash(subject), StringHash(label))
             else
                call Save$NAME$Handle( ht, StringHash(subject), StringHash(label), value)
             endif
         endfunction

         function GetTable$NAME$ takes string subject, string label returns $TYPE$
             return Load$NAME$Handle( ht, StringHash(subject), StringHash(label))
         endfunction
    //! endtextmacro
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Player","player")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Widget","widget")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Destructable","destructable")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Item","item")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Unit","unit")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Ability","ability")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Timer","timer")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Trigger","trigger")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("TriggerCondition","triggercondition")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("TriggerAction","triggeraction")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("TriggerEvent","event")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Force","force")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Group","group")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Location","location")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Rect","rect")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("BooleanExpr","boolexpr")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Sound","sound")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Effect","effect")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("UnitPool","unitpool")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("ItemPool","itempool")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Quest","quest")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("QuestItem","questitem")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("DefeatCondition","defeatcondition")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("TimerDialog","timerdialog")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Leaderboard","leaderboard")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Multiboard","multiboard")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("MultiboardItem","multiboarditem")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Trackable","trackable")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Dialog","dialog")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Button","button")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("TextTag","texttag")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Lightning","lightning")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Image","image")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Ubersplat","ubersplat")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Region","region")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("FogState","fogstate")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("FogModifier","fogmodifier")
    //! runtextmacro FAUX_HANDLE_VARS_GetTableHandle("Hashtable","hashtable")

    function CS_KillTrigger takes trigger t returns nothing
    // let em leak
    endfunction

    // too bad the Handle vars' old functionality forces me to make these things
    // inline-unfriendly
    function AttachObject takes agent subject,  string label, agent value returns nothing
        call SetTableObject( GetAttachmentTable(subject) , label, value)
    endfunction




    function GetAttachedHandle takes agent subject, string label returns agent
        debug call BJDebugMsg("[debug] What the heck? Why would you call HandleHandle I guess this was caused by a search and replace mistake")
        return null
    endfunction

    // these are inline friendly, ok, maybe they aren't because jasshelper does not recognize
    // GetHandleId as non-state changing. But they will be once I fix jasshelper...



    // got bored so I now use a textmacro...
    //! textmacro FAUX_HANDLE_VARS_GetAttachedHandle takes NAME, TYPE
         function Attach$NAME$ takes agent subject,  string label, $TYPE$ value returns nothing
             call SetTable$NAME$( GetAttachmentTable(subject), label, value)
         endfunction

         function GetAttached$NAME$ takes agent subject, string label returns $TYPE$
             return GetTable$NAME$( GetAttachmentTable(subject), label)
         endfunction
    //! endtextmacro
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Player","player")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Widget","widget")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Destructable","destructable")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Item","item")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Unit","unit")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Ability","ability")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Timer","timer")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Trigger","trigger")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("TriggerCondition","triggercondition")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("TriggerAction","triggeraction")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("TriggerEvent","event")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Force","force")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Group","group")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Location","location")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Rect","rect")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("BooleanExpr","boolexpr")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Sound","sound")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Effect","effect")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("UnitPool","unitpool")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("ItemPool","itempool")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Quest","quest")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("QuestItem","questitem")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("DefeatCondition","defeatcondition")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("TimerDialog","timerdialog")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Leaderboard","leaderboard")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Multiboard","multiboard")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("MultiboardItem","multiboarditem")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Trackable","trackable")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Dialog","dialog")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Button","button")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("TextTag","texttag")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Lightning","lightning")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Image","image")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Ubersplat","ubersplat")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Region","region")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("FogState","fogstate")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("FogModifier","fogmodifier")
    //! runtextmacro FAUX_HANDLE_VARS_GetAttachedHandle("Hashtable","hashtable")
  
  
    private function init takes nothing returns nothing
        set ht=InitHashtable()
    endfunction

endlibrary
Where I need to Put each of this?
 
Level 39
Joined
Feb 27, 2007
Messages
5,036
New triggers; can be one each or just one for all 3, doesn't matter. It may give you a warning when you enable the triggers but you can safely ignore it (JNGP bypasses this warning). I actually attached the map to my above post now.
 
Status
Not open for further replies.
Top