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