Name | Type | is_array | initial_value |
//TESH.scrollpos=0
//TESH.alwaysfold=0
The very first required thing is NewGen WE. If it is not currently installed, be sure to
get it.
hiveworkshop.com/forums/general-mapping-tutorials-278/setting-up-newgen-we-intro-trigger-editor-188349/
Read chapter 1 through to the bottom as there are final tweaks at the end.
//TESH.scrollpos=0
//TESH.alwaysfold=0
Settings:
Systems
Encoder Trigger
- Manages settings for the Encoder system
Encryption Trigger
- Manages encryption settings for Scrambler
SaveLoad
Catalogs
- Manages catalogs
Encoder Settings
- Manages settings like the code key and security value as well as
- the current Encoder version (backwards compatibility)
Encoders
- Manages built Encoders. Encoders determine the order values are saved/loaded
- in as well as what values can be saved/loaded. It is the frame for everything else.
OnLoad Settings
- Has events for OnLoad as well as various -load settings and popular
- user defined settings.
OnSave
- Has two events, one for when a player types -save and the other for displaying
- the code.
//TESH.scrollpos=218
//TESH.alwaysfold=0
/********************************************************************************************
*
* Each Encoder object initially has to be built. Building them is extremely easy.
*
********************************************************************************************/
local Encoder encoder = BuildEncoder(12,24,1) //min code length 12
//max code length 24
//encoder version 1
/********************************************************************************************
*
* BuildEncoder takes three values
*
* - The minimum size a code can be
* - The maximum size a code can be
*
* These two values prevent a user from typing in a code out of bounds.
* For example, it will stop a user from typing -load 5 or
* -load m3958034958093458092tntn4o34j9uvt483to4jviojtv4iotergfergj
*
* The reason for boundaries is that much smaller codes have a much
* higher chance of getting through as a valid code.
*
* - The encoder version
*
* The encoder version number. Encoder version numbers allow for backwards
* compatible codes. The first encoder would be version 1, the next would
* be version 2. These are used during -load and are outputted at -save.
*
* Slots for storing values are created in Encoder objects via CodeRange objects.
* These CodeRange objects are essentially slots that can hold a specific range of numbers.
*
********************************************************************************************/
Encoder
[0-15]
[0-15]
[100-200]
[-593-1429]
/********************************************************************************************
*
* The first slot would be able to hold values between 0 and 15. The next could hold between
* 0 and 15, then after is 100 to 200, and then -593 to 1429.
*
* Encoder objects can have lots of slots. Example of an Encoder that has a slot for
* storing gold (remember gold can go from 0 to 1,000,000)
*
********************************************************************************************/
Encoder
[0-1000000]
/********************************************************************************************
*
* These slots are created with what are called CodeRange objects.
*
* Think of an Encoder object as this skeleton that can have all of these slots added to it,
* this way when you do your saving and loading, you can read/write to these slots. This allows
* one to pretty much save whatever they want to save.
*
* So looking back
* local CodeRange range = CodeRange.create(0, 1)
*
* That would create a CodeRange object called range that can hold values between 0 and 1.
*
* Many CodeRange objects can be generated, and they can be named pretty much whatever
*
********************************************************************************************/
local CodeRange gold = CodeRange.create(0, 1000000)
local CodeRange lumber = CodeRange.create(0, 1000000)
/********************************************************************************************
*
* It is rather simple to add a CodeRange to an Encoder
*
********************************************************************************************/
call encoder.add(gold)
call encoder.add(lumber)
/********************************************************************************************
*
* Specific ranges within slots can be linked to other slots, and these links can be given custom ids.
* The ids are important as the links may or may not be used. For example, not all items have item charges,
* so the id can be used in saving and loading to see if the current open slot is an item charge slot.
*
********************************************************************************************/
//! textmacro ENCODER_1
private function Encoder1 takes nothing returns nothing
local Encoder encoder = BuildEncoder(12,24,1) //min code length 12
//max code length 24
//encoder version 1
local CodeRange item = CodeRange.create(0, 100)
local CodeRange itemCharge = CodeRange.create(1, 99)
/****************************************************************
*
* low bound | high bound | slot to be linked to | custom id
* 1 | 15 | itemCharge | 1
*
****************************************************************/
call item.link(1, 15, itemCharge, 1) //link items between 1 and 15 to itemCharge
//this means that items between 1 and 15 get
//an item charge.
call encoder.add(item)
//item is linked to itemCharge, so itemCharge isn't added
endfunction
//! endtextmacro
/********************************************************************************************
*
* The linka method can also be used, which means link all. This essentially links the entire range.
*
********************************************************************************************/
//! textmacro ENCODER_1
private function Encoder1 takes nothing returns nothing
local Encoder encoder = BuildEncoder(12,24,1) //min code length 12
//max code length 24
//encoder version 1
local CodeRange item = CodeRange.create(0, 100)
local CodeRange itemCharge = CodeRange.create(1, 99)
//slot to be linked to
// itemCharge
call item.linka(itemCharge) //all items get an item charge
call encoder.add(item)
endfunction
//! endtextmacro
/********************************************************************************************
*
* Some interesting algorithms can be used when doing links. For example, when saving a 6 slot inventory,
* there are two ways to do it-
*
********************************************************************************************/
//! textmacro ENCODER_1
private function Encoder1 takes nothing returns nothing
local Encoder encoder = BuildEncoder(12,24,1) //min code length 12
//max code length 24
//encoder version 1
local CodeRange hero = CodeRange.create(1, 25)
local CodeRange item = CodeRange.create(0, 600)
call hero.linka(item)
call hero.linka(item)
call hero.linka(item)
call hero.linka(item)
call hero.linka(item)
call hero.linka(item)
call encoder.add(hero)
endfunction
//! endtextmacro
/********************************************************************************************
*
* The above would link 6 items to a hero. However, if the heroes inventory is empty, this means that 6
* empty slots end up being saved.
*
* Another smarter way to do this wold be
*
********************************************************************************************/
//! textmacro ENCODER_1
private function Encoder1 takes nothing returns nothing
local Encoder encoder = BuildEncoder(12,24,1) //min code length 12
//max code length 24
//encoder version 1
local CodeRange hero = CodeRange.create(1, 25)
local CodeRange item = CodeRange.create(0, 600)
local CodeRange item2 = CodeRange.create(0, 600)
local CodeRange item3 = CodeRange.create(0, 600)
local CodeRange item4 = CodeRange.create(0, 600)
local CodeRange item5 = CodeRange.create(0, 600)
local CodeRange item6 = CodeRange.create(0, 600)
call item.link(1,600,item2,1)
call item2.link(1,600,item3,1)
call item3.link(1,600,item4,1)
call item4.link(1,600,item5,1)
call item5.link(1,600,item6,1)
call hero.linka(item)
call encoder.add(hero)
endfunction
//! endtextmacro
/********************************************************************************************
*
* hero -> item -> item2 -> item3 -> item4 -> item5 -> item6
*
* The links between each item go between the range of 1 and 600. If an item is null (a value of 0),
* then the link isn't entered. This means that if an inventory was empty, only 1 item would be
* saved (initial link).
*
* Notice that the lower bounds of the ranges are sometimes 0 or sometimes 1 for the
* catalogs (like heroes and items). A range starting at 0 means that the value can possibly be
* null, like an empty inventory (6 null items). Hero starts at 1 because a player will always have a
* hero, so this value can never ever be null.
*
* Infinite values can also be saved-
*
********************************************************************************************/
call item.link(1,100,item,1)
/********************************************************************************************
*
* That would essentially keep saving items until it comes across a null item. This can be useful when
* saving an unknown amount of a set of values.
*
* Please note that when it comes to catalogs, a catalog count is usually used for the code ranges and
* sub range variables are used for the links-
*
********************************************************************************************/
local CodeRange hero = CodeRange.create(0, HeroCatalog1.count)
local CodeRange items = CodeRange.create(0, ItemCatalog1.count)
local CodeRange items2 = CodeRange.create(0, ItemCatalog1_2.count)
call hero.link(HeroCatalog1.HERO_1_0, HeroCatalog1.HERO_1_1, items, 0)
call hero.link(HeroCatalog1.HERO_2_0, HeroCatalog1.HERO_2_1, items2, 0)
/********************************************************************************************
*
* The very last range added to an Encoder object can't link to anything and should
* be the biggest range.
*
********************************************************************************************/
//TESH.scrollpos=135
//TESH.alwaysfold=0
/********************************************************************************************
*
* Values are written into a buffer in saving. The buffer is created from the latest Encoder
* object.
*
* The buffer expects the values that the Encoder object expects.
*
********************************************************************************************/
//! textmacro ENCODER_1
private function Encoder1 takes nothing returns nothing
local Encoder encoder = BuildEncoder(12,24,1) //min code length 12
//max code length 24
//encoder version 1
local CodeRange item = CodeRange.create(0, 100)
local CodeRange itemCharge = CodeRange.create(1, 99)
//slot to be linked to
// itemCharge
call item.linka(itemCharge) //all items get an item charge
call encoder.add(item)
endfunction
//! endtextmacro
/********************************************************************************************
*
* With this Encoder object, the buffer would expect two values, the first being between 0 and 100
* and the second being between 1 and 99.
*
* This could be a possibility-
*
********************************************************************************************/
private function Save takes nothing returns boolean
local player triggerPlayer = GetTriggerPlayer()
local integer playerId = GetPlayerId(triggerPlayer)
local DataBuffer buffer = GetCurrentEncoder().write(playerId)
call buffer.write(50) //the item, between 0 and 100
call buffer.write(30) //the item charge, between 1 and 99
call DisplayTimedTextToPlayer(triggerPlayer, 0, 0, 60, "Encoder Version: " + GetCurrentEncoder().toString())
call DisplayTimedTextToPlayer(triggerPlayer, 0, 0, 60, buffer.code)
return false
endfunction
/********************************************************************************************
*
* Another possibilty would be
*
********************************************************************************************/
//! textmacro ENCODER_1
private function Encoder1 takes nothing returns nothing
local Encoder encoder = BuildEncoder(12,24,1) //min code length 12
//max code length 24
//encoder version 1
local CodeRange hero = CodeRange.create(1, 25)
local CodeRange item = CodeRange.create(0, 600)
local CodeRange item2 = CodeRange.create(0, 600)
local CodeRange item3 = CodeRange.create(0, 600)
local CodeRange item4 = CodeRange.create(0, 600)
local CodeRange item5 = CodeRange.create(0, 600)
local CodeRange item6 = CodeRange.create(0, 600)
call item.link(1,600,item2,1)
call item2.link(1,600,item3,1)
call item3.link(1,600,item4,1)
call item4.link(1,600,item5,1)
call item5.link(1,600,item6,1)
call hero.linka(item)
call encoder.add(hero)
endfunction
//! endtextmacro
private function Save takes nothing returns boolean
local player triggerPlayer = GetTriggerPlayer()
local integer playerId = GetPlayerId(triggerPlayer)
local DataBuffer buffer = GetCurrentEncoder().write(playerId)
call buffer.write(12) //the hero, between 1 and 25)
call buffer.write(32) //the item, between 0 and 600
call buffer.write(0) //the item, between 0 and 600
call DisplayTimedTextToPlayer(triggerPlayer, 0, 0, 60, "Encoder Version: " + GetCurrentEncoder().toString())
call DisplayTimedTextToPlayer(triggerPlayer, 0, 0, 60, buffer.code)
return false
endfunction
/********************************************************************************************
*
* The reason not all of the items were written is because of that 0 and the way the links
* were set up.
*
* The slot where the 0 was written to is item2. The link between item2 and item3 requires a
* value between 1 and 600. Because 0 isn't in that range, the code stops at item2. Another
* example using the same Encoder.
*
********************************************************************************************/
private function Save takes nothing returns boolean
local player triggerPlayer = GetTriggerPlayer()
local integer playerId = GetPlayerId(triggerPlayer)
local DataBuffer buffer = GetCurrentEncoder().write(playerId)
call buffer.write(12) //the hero, between 1 and 25)
call buffer.write(32) //the item, between 0 and 600
call buffer.write(500) //the item, between 0 and 600
call buffer.write(300) //the item, between 0 and 600
call buffer.write(0) //the item, between 0 and 600
call DisplayTimedTextToPlayer(triggerPlayer, 0, 0, 60, "Encoder Version: " + GetCurrentEncoder().toString())
call DisplayTimedTextToPlayer(triggerPlayer, 0, 0, 60, buffer.code)
return false
endfunction
/********************************************************************************************
*
* In this case, the 0 is written into item4. It is also possible that no 0s are
* written, meaning that the inventory is full.
*
********************************************************************************************/
private function Save takes nothing returns boolean
local player triggerPlayer = GetTriggerPlayer()
local integer playerId = GetPlayerId(triggerPlayer)
local DataBuffer buffer = GetCurrentEncoder().write(playerId)
call buffer.write(12) //the hero, between 1 and 25)
call buffer.write(32) //the item, between 0 and 600
call buffer.write(500) //the item, between 0 and 600
call buffer.write(300) //the item, between 0 and 600
call buffer.write(100) //the item, between 0 and 600
call buffer.write(52) //the item, between 0 and 600
call buffer.write(253) //the item, between 0 and 600
call DisplayTimedTextToPlayer(triggerPlayer, 0, 0, 60, "Encoder Version: " + GetCurrentEncoder().toString())
call DisplayTimedTextToPlayer(triggerPlayer, 0, 0, 60, buffer.code)
return false
endfunction
/********************************************************************************************
*
* In this case, all 6 items are written, no 0s.
*
* Ids can also be used to see if slots exist.
*
********************************************************************************************/
if (buffer.id == ITEM_CHARGE ) then
call buffer.write(GetItemCharges(someItem))
endif
/********************************************************************************************
*
* Saving and loading require coding knowledge.
*
* If you noticed, there is only one Save function. The framework will always save using the
* latest Encoder version. However, if you look, the same concept for Catalogs and Encoders is
* used for loading, thus allowing one to load from older codes.
*
* In the case of loading, a new Loader must be used for a new Encoder, even if that Loader is
* identical to the old one.
*
* In the case of Loaders, values are read from the buffer, so buffer.read is used.
*
* For example, reading from a code using this Encoder-
*
********************************************************************************************/
//! textmacro ENCODER_1
private function Encoder1 takes nothing returns nothing
local Encoder encoder = BuildEncoder(12,24,1) //min code length 12
//max code length 24
//encoder version 1
local CodeRange gold = CodeRange.create(0, 1000000)
local CodeRange lumber = CodeRange.create(0, 1000000)
call encoder.add(gold)
call encoder.add(lumber)
endfunction
//! endtextmacro
//! textmacro LOAD_1
private function Load1 takes nothing returns nothing
local player triggerPlayer = GetTriggerPlayer()
local integer playerId = GetPlayerId(triggerPlayer)
local DataBuffer buffer = bf
call SetPlayerState(triggerPlayer, PLAYER_STATE_RESOURCE_GOLD, buffer.read())
call SetPlayerState(triggerPlayer, PLAYER_STATE_RESOURCE_LUMBER, buffer.read())
endfunction
//! endtextmacro
/********************************************************************************************
*
* buffer.id may be used to check if slots exist or not as before
*
********************************************************************************************/
if (buffer.id == ITEM_CHARGE) then
call SetItemCharges(myItem, buffer.read())
endif
/********************************************************************************************
*
* Example of loading a hero
*
********************************************************************************************/
local unit u = CreateUnit(HeroCatalog1[buffer.read()].raw,triggerPlayer,x,y,0)
/********************************************************************************************
********************************************************************************************/
//TESH.scrollpos=0
//TESH.alwaysfold=0
hiveworkshop.com/forums/general-mapping-tutorials-278/quickstart-guide-installing-lua-scripts-easy-195612/
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*************************************************************************************
*
* Requires Lua
*
* Go to Quickstart Guide to Lua and follow link to read how to install
*
*************************************************************************************/
System for saving xp-
hiveworkshop.com/forums/jass-functions-413/snippet-unit-state-percent-189943/
//TESH.scrollpos=36
//TESH.alwaysfold=0
library Encoder /* v3.0.1.2
*************************************************************************************
*
* Save/Load system
*
*************************************************************************************
* */uses/*
*
* */ BigInt /* hiveworkshop.com/forums/jass-functions-413/system-bigint-188973/
* */ QueueQueue /* hiveworkshop.com/forums/submissions-414/snippet-queuequeue-190890/
*
* These two can be changed (*Advanced*)
* */ KnuthChecksum /* hiveworkshop.com/forums/1846246-post343.html
* */ Scrambler /* hiveworkshop.com/forums/submissions-414/snippet-salt-189766/
*
* Used in settings functions:
* private function Checksum takes BigInt k, integer m returns integer
* private function ApplyScramble takes BigInt k, integer pid returns nothing
* private function UnapplyScramble takes BigInt k, integer pid returns nothing
*
************************************************************************************
*
* SETTINGS
*/
private keyword b10 //base 10
globals
/*************************************************************************************
*
* I suggest permutation of the following base for encoders
*
* 0123456789ABCDEFGHKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@#$%&?
*
*************************************************************************************/
/*************************************************************************************
*
* VER_BASE refers to the base used with encoder.toString()
*
*************************************************************************************/
private constant string VER_BASE="0123456789"
/*************************************************************************************
*
* Coloring Settings
*
*************************************************************************************/
private constant string NUM_COLOR="|cff40e0d0" //what to color numbers
private constant string LOWER_COLOR="|cffff69b4" //what to color lowercase characters
private constant string UPPER_COLOR="|cff00AA00" //what to color uppercase characters
private constant string SPEC_COLOR="|cffffff00" //what to color special characters
private constant string DELIMITER_COLOR="|cffffffff" //what to color DELIMITER characters
/*************************************************************************************
*
* Spacing Settings
*
*************************************************************************************/
private constant string DELIMITER="-" //DELIMITER to make code easier to read
private constant integer DELIMITER_COUNT=4 //how many characters per DELIMITER
/*************************************************************************************
*
* Encryption Settings
*
*************************************************************************************/
/*************************************************************************************
*
* SHUFFLES
*
* How many shuffles to perform in encoder
*
* Nust be greater than 0
*
*************************************************************************************/
private constant integer SHUFFLES=3
/*************************************************************************************
*
* CHECKSUM_VARIANCE
*
* Balanced value: .85
*
* The larger the variance value, the smaller the range of active checksums. A small range
* means that it is more likely for two players to have the same checksums.
*
* Smaller variance increases the range of active checksums, however it also increases the
* range of cehcksum strengths. This means that some checksums may be much weaker than others,
* which increases the chances for those players with weaker checksums to tamper with their
* code.
*
* Checksum strength should be about the same for each checksum and there should be enough
* active checksums that it is unlikely that two players will have te same checksum.
*
* .85 is a rather balanced value, but it can be increased for generally stronger checksums
* with smaller ranges or decreased for weaker checksums with wider ranges.
*
* Ex:
* .85 for a checksum of 238,609,294
*
* min: 202,817,899
* range: 35,791,395
*
* 1 in 35,791,395 checksums will work for a player and checksums will all be around same
* strength.
*
* .99 for a checksum of 238,609,294
*
* min: 236,223,201
* range: 2,386,093
*
* 1 in 2,386,093 checksums will work for a player and checksums will all be around same
* strength.
*
* .01 for a checksum of 238,609,294
*
* min: 2,386,092
* range: 236,223,202
*
* 1 in 236,223,202 will work for a player and checksums will have a wide range of strengths
* from weak to strong.
*
*************************************************************************************/
private constant real CHECKSUM_VARIANCE=.85
/*************************************************************************************
*
* PLAYER_CHECKSUM_SALT
*
* Player checksum salt refers to a value that is appended to a player's name when
* generating player hashes. A player's checksum salt helps determine the player's
* checksum for encoders.
*
* This value can be any string
*
* example: "29a\\~alf1!m~..."
*
*************************************************************************************/
private constant string PLAYER_CHECKSUM_SALT=""
endglobals
/*************************************************************************************
*
* Checksum
*
* This is the Checksum used for code security (makes modifications
* difficult). By default, this uses the Knuth Checksum, but
* that can be changed.
*
* BigInt k: number to get the checksum for
* integer m: dividend for modulos
*
* returns: nothing
*
*************************************************************************************/
private function Checksum takes BigInt k, integer m returns integer
return GetKnuthChecksum(k, m)
endfunction
/*************************************************************************************
*
* ApplyScramble
*
* This is essentially for scrambling the code given a player id.
* By default this uses my own scrambling algorithm.
* Because a player hash based on the player's username is used in my
* scrambling algorithm, this will make it so that a player can't load
* up the code of another player with 0 increase to the code size.
* This security alone is not enough to 100% guarantee the player unique
* codes.
*
* BigInt k: number to be scrambled
* integer pid: player id to scramble for
*
* returns: nothing
*
*************************************************************************************/
private function ApplyScramble takes BigInt k, integer pid returns nothing
call Shuffle(k, pid, SHUFFLES)
endfunction
/*************************************************************************************
*
* UnapplyScramble
*
* This is used to undo the scrambling on a number. This should
* revert the number back to what it was before it was scrambled.
*
* BigInt k: number to be unscrambled
* integer pid: player id to unscramble the number for
*
* returns: nothing
*
*************************************************************************************/
private function UnapplyScramble takes BigInt k, integer pid returns nothing
call Unshuffle(k, pid, SHUFFLES)
endfunction
/*
*******************************************************************
*
* struct CodeRange extends array
*
* - A slot that can store a value. These slots have a range of values they can store. The
* - range is from a low bound value to a high bound value. Slots are added to Encoder objects.
* - Specific ranges of slots can link to other slots (an item with 25 charges for example). Not
* - all slots can store values: some are purely pointers (an inventory slot for example which simply
* - points to 6 items).
*
* static method create takes integer lowBound, integer highBound returns CodeRange
* - Creates a new CodeRange that can be added to an Encoder and linked to. CodeRange objects
* - can be linked to multiple times and can link to as many other CodeRange objects as needed.
* - If the lowBound is equal to the highBound, then the CodeRange object returned is a pointer
* - object that can't store values. This can be useful for things like an inventory that simply
* - points to 6 item slots.
*
* - integer lowBound The minimum value that can be stored in the slot.
* - integer highBound The maximum value that can be stored in the slot.
* method link takes integer lowBound, integer highBound, CodeRange linkTo, integer customLinkId returns nothing
* - Links a CodeRange to another CodeRange. The link is only applied if the value that ends
* - up going into the CodeRange fits the link range. For example, if only heroes from 1 to 5 had
* - an inventory of 6, then the lowBound would be 1 and the highBound would be 5 for that link.
* - Passing in the minimal value and maximal values for a given slot does a link for all possible
* - values that can go into that slot.
*
* - integer lowBound The minimal value that can be in the slot to go into the link
* - integer highBound The maximal value that can be in the slot to go into the link
* - CodeRange linkTo The slot that is to be linked to
* - integer customLinkId A link id that can be used to infer current slot for save/load
* method linka takes CodeRange linkTo returns nothing
* - Links all possible values to a slot. Essentially just calls link with the minimum possible
* - value, maximum possible value, and a custom id of 0.
*
* - CodeRange linkTo The slot that is to be linked to
*
************************************************************************************
*
* struct DataBuffer extends array
*
* - The DataBuffer is used for reading and writing values.
* - When opening an Encoder, it is loaded into the DataBuffer
* - and then values can be read/written. An Encoder may be
* - opened for decompressing a code or for compressing a
* - collection of numbers into a code.
*
* readonly integer id
* - Returns the current custom link id (remember link and linka in CodeRange)
* - As slots inside of links may or may not exist (does value fit?), this is
* - a necessity so that a user can easily determine whether they are in a
* - questionable slot or not.
* - Can be used in read and write mode.
* readonly string code
* - Returns all of the values in the DataBuffer as a save/load code.
* - Can only be used when the DataBuffer is finalized.
* - Can be used in write mode.
*
* method write takes integer value returns nothing
* - Writes a value to the DataBuffer. Order of values is determined by the
* - loaded Encoder. For example, if a Hero, gold, and lumber slots were
* - added to the Encoder in that order, then the DataBuffer would expect
* - values fitting those ranges in that order. When all values are written,
* - the DataBuffer is finalized (meaning can't be written to) and the code
* - can be read.
* - Can be used in write mode.
*
* - integer value The value to write to the DataBuffer.
* method read takes nothing returns integer
* - Reads a value out of the DataBuffer. Value read order is determined by
* - the loaded Encoder.
* - Can be used in read mode.
*
************************************************************************************
*
* struct Encoder extends array
*
* - An Encoder is like a frame for compacting values. It is used
* - for storing base, checksum, player checksum, and CodeRange information.
* - the Encoder determines the order of values in a code for the DataBuffer
* - as well. DataBuffers can only be opened through an Encoder.
*
* static method create takes string base, integer minCodeLength, integer maxCodeLength, integer maxChecksum, integer encoderVersion returns Encoder
* - Creates a new Encoder.
*
* - string base The collection of possible characters that the Encoder can
* - use for its save/load codes. Bigger collection means smaller
* - codes.
* - integer minCodeLength Minimum length a code has to be to be loaded. Useful for blocking
* - small values like 1 or 5. Keeps load codes in bounds.
* - integer maxCodeLength Maximal length a code can be to be loaded. Useful for blocking
* - off random large values. Keeps load codes in bounds.
* - integer maxChecksum The maximum checksum value that can be put into the Encoder.
* - Checksums are used to validate codes (ensure they weren't
* - tampered with and that there are no typos in it). The bigger
* - the checksum value, the more secure the code is, but the longer
* - the code will be. I typically use a 6 digit number like 148292
* - or 559321.
* - Maximum checksum- 238609294
* -
* - integer encoderVersion Used for version control on encoders. toString returns this value and
* - convertString takes the toString value and converts it back into the encoder
* - toString() -> encoderVersion
* - convertString(encoderVersion)
* method toString takes nothing returns string
* - Converts the Encoder into a string that represents it. This can
* - be outputted to players to show the Encoder version that their
* - code was saved for. Players can then possibly type in the Encoder
* - version of older codes so that older codes can be loaded.
* static method convertString takes string encoderId returns Encoder
* - Converts an Encoder string into an Encoder. Used primarily for
* - older save/load codes (player might have inputted Encoder id
* - for their save/load code).
*
* - string encoderId The string that represents the Encoder (toString).
* method add takes CodeRange valueSlot returns nothing
* - Adds a new slot to the Encoder. Value order doesn't matter.
*
* - CodeRange valueSlot The CodeRange to be added to the Encoder.
* method read takes string codeString, integer loadingPlayerId returns DataBuffer
* - Opens a DataBuffer for reading and returns the opened DataBuffer. Loads
* - a code string into the DataBuffer. If this returns 0, the code was invalid.
*
* - string codeString The code to load into the DataBuffer
* - integer loadingPlayerId The player id to load the code for (must
* - be a valid human playing player).
* method write takes integer savingPlayerId returns DataBuffer
* - Opens a DataBuffer for writing and returns the opened DataBuffer. If
* - this returns 0, the Encoder or player id were invalid.
*
* - integer savingPlayerId The player id to save the code for (must
* - be a valid human playing player).
*
************************************************************************************/
/*************************************************************************************
*
* Code
*
*************************************************************************************/
globals
private keyword Link
/*************************************************************************************
*
* Encoder Variables
*
*************************************************************************************/
private Table array eb //encoder base
private string array er //encoder code string
private Table array eh //encoder max hash value
private Base es //encoder base for code string
private Link array el //last range added to encoder
private integer array ec //encoder ver to encoder
private integer array ml //minimum code length
private integer array mx //maximum code length
/*************************************************************************************
*
* Range Variables
*
*************************************************************************************/
private integer array rl //low bound
private integer array rh //high bound
private integer array rsh //shifted high bound
private integer array rf //flag
private constant integer AP=1 //always positive
private constant integer SN=2 //sometimes negative
private constant integer AN=3 //always negative
/*************************************************************************************
*
* Link Variables
*
*************************************************************************************/
private integer array li //link id
private boolean array lb //is link
/*************************************************************************************
*
* Player Variables
*
*************************************************************************************/
private integer array ph //hash of player name + salt
private integer array pn //next player
/*************************************************************************************
*
* Data Buffer Variables
*
*************************************************************************************/
//Base
private Base b10=0 //use for writing
//use encoder base for reading
private Link array dm //data buffer looper
private integer dc=0 //data buffer count
private integer array dn //data buffer next, recycler
private integer array dl //data buffer previous
private integer array dv //data buffer value
private Link array di //data buffer link node id
private integer array dd //data buffer node
private integer array dz //data buffer link id
private integer array de //data buffer encoder
private boolean array df //is data buffer finalized?
private boolean array dw //data buffer open for writing?
private integer array dp //data buffer player
endglobals
/*************************************************************************************
*
* Link
*
* Links values together to form dynamic objects.
*
*************************************************************************************/
private struct Link extends array
implement QueueQueue
implement QueueQueueLoop
endstruct
/*************************************************************************************
*
* CodeRange : Link
*
* Used for manipulating value slots in Encoders and CodeRanges.
*
* Methods
* static method create takes integer l, integer h returns CodeRange
* method link takes integer l, integer h, CodeRange p, integer i returns nothing
* method linka takes CodeRange p returns nothing
*
*************************************************************************************/
struct CodeRange extends array
/*************************************************************************************
*
* create
* Creates a code range given a maximum value and a minimum value. Ranges are slots
* that can be added to encoders and other Ranges.
*
* integer l: low bound
* integer h: high bound
*
* returns: CodeRange
*
*************************************************************************************/
static method create takes integer l,integer h returns CodeRange
local Link t
if (h>l) then
//first ensure that the high bound is greater than the low bound
// if the high bound is greater, then it is a valid range for
// storing actual values
//instantiate
set t=Link.allocate()
set t.skips=false
//store the low bound and the high bound into the properties
set rl[t]=l //low bound
set rh[t]=h //high bound
//now have to determine how to store the value
//the value could be negative, it could always be negative,
//or it could always be positive
if (0>h) then
/**********************************
*
* Flag: AN
* Shifted Low Bound: High Bound
* Shifted High Bound: -Low Bound + High Bound
*
**********************************/
set rf[t]=AN //flag to always negative
//the shifhted high bound is the low bound minus
//the high bound
set rsh[t]=-l+h+1
elseif (0>l) then
/**********************************
*
* Flag: SN
* Shifted Low Bound: low bound
* Shifted High Bound: high bound - low bound
*
**********************************/
set rf[t]=SN //flag to sometimes negative
set rsh[t]=h-l+1
else
/**********************************
*
* Flag: AP
* Shifted Low Bound: lowBound
* Shifted High Bound: highBound-lowBound
*
**********************************/
set rf[t]=AP
set rsh[t]=h-l+1
endif
return t
elseif (h==l) then
//if they are equal, then it is a valid pointer range.
// pointer ranges are used to just point to values. They don't go into the actual code,
// but values they point to do.
/**********************************
*
* Flag: 0
*
**********************************/
//simple instiate it as it is meant only for pointing
return Link.allocate()
debug else
//if the high bound is lower than the low bound, then the range isn't valid
// throw an error
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"CODE RANGE CREATE ERROR: HIGH BOUND LOWER THAN LOW BOUND")
debug return 0
endif
return 0
endmethod
/*************************************************************************************
*
* link
* Links a CodeRange to another CodeRange by adding the later range to the
* first range by a pointer.
*
* integer l: low bound
* integer h: high bound
* integer p: CodeRange to add to CodeRange this
* integer i: a user id for identifying the link
*
* returns: nothing
*
*************************************************************************************/
method link takes integer l,integer h,CodeRange p,integer i returns nothing
local Link t
debug if (h>=l and h<=rh[this] and l>=rl[this]) then
//first ensure that the high bound is greater than the low bound
// if the high bound is greater, then it is a valid range for
// storing actual values
//instantiate via point
set t=Link(this).point(p)
set t.skips=false
set rl[t]=l //link low bound
set rh[t]=h //link high bound
set li[t]=i //link id for identification
set lb[t]=true //is link
debug else
//if the high bound is lower than the low bound, then the range isn't valid
// throw an error
debug if (l>h) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"CODE RANGE CREATE ERROR: HIGH BOUND LOWER THAN LOW BOUND")
debug endif
//range was out of bounds
debug if (h>rh[this] or l<rl[this]) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"CODE RANGE CREATE ERROR: LINK RANGE OUT OF BOUNDS")
debug endif
debug endif
endmethod
/*************************************************************************************
*
* linka
* A special link that links all values (lowBound to highBound).
*
* integer p: CodeRange to add to CodeRange this
*
* returns: nothing
*
*************************************************************************************/
method linka takes CodeRange p returns nothing
call link(rl[this],rh[this],p,0)
endmethod
endstruct
/*************************************************************************************
*
* DataBuffer : Queue, Link
*
* Used to read/write code.
*
* Properties
* readonly integer id
*
* Methods
* method write takes integer value returns nothing
* method operator code takes nothing returns string
* method read takes nothing returns integer
* internal method open takes nothing returns nothing
*
*************************************************************************************/
private keyword open
struct DataBuffer extends array
/*************************************************************************************
*
* id
* Retrieves the current open id on the DataBuffer
*
*************************************************************************************/
method operator id takes nothing returns integer
return dz[this]
endmethod
/*************************************************************************************
*
* internal open
* Prepares next slot in DataBuffer
*
* takes: nothing
*
* returns: nothing
*
*************************************************************************************/
method open takes nothing returns nothing
local Link n
local Link y=0
local Link l=0
//retrieve the next node
set n=dm[this].get //node
loop
//if the current node is a pointer, have to determine
// whether to go inside of the pointer or not.
exitwhen not lb[n]
loop
//keep looping until can either go inside of link or the
// node isn't a link
//link ranges of 0 to 0 are pointer links, all values fit into them
exitwhen not lb[n] or (0==rl[n] and 0==rh[n]) or 0==n
//if not a pointer link, then have to retrieve the parent
// node's value
set y=dm[this].depthPointer
loop
exitwhen not lb[y.depthNode]
set y=y.depthPointer
endloop
//the value is stored into the depth pointer's id, check for fit
exitwhen (y.id>=rl[n] and y.id<=rh[n])
set n=dm[this].skip //if value doesn't fit, skip link and all of its contents
endloop
//if the final found node was a pointer (meaning the value fit)
//go inside of it
if (lb[n]) then
//only store link for node ref if the link id isn't 0
if (0!=li[n]) then
set l=n
endif
set n=dm[this].get //go to next node
endif
endloop
//if there is no next node, finalize
if (0==n) then
set df[this]=true
return
endif
//add new node to data buffer
if (0==dn[0]) then
set dc=dc+1
set y=dc
else
set y=dn[0]
set dn[0]=dn[y]
endif
//this buffer is a list because it will eventually have to be looped
//over backwards. The buffer has to be read into the code backwards
//or it will be impossible to read it out because some values exist
//and some don't. Remember integers are written left to right but
//read right to left.
set dl[y]=dl[this]
set dn[y]=this
set dn[dl[y]]=y
set dl[this]=y
//set link id for current node to the node right above it, which
// is always going to be a link
set di[y]=li[l]
set dz[this]=li[l]
//set node for size reference and shifting
set dd[y]=n
endmethod
/*************************************************************************************
*
* write
* Writes a value to the DataBuffer
*
* integer value: value to write
*
* returns: nothing
*
*************************************************************************************/
method write takes integer v returns nothing
local Link y=dl[this]
local Link n=dd[y]
//make sure
// buffer isn't finalized
// buffer is open for writing
// buffer is valid
debug if (not df[this] and dw[this] and 0!=de[this] and v>=rl[n] and v<=rh[n]) then
//store shifted value as current depth pointer id
set dm[this].depthPointer.id=v
//shift the value so that it is smaller
if (rf[n]==AN) then
set v=-v+rh[n]
else
set v=v-rl[n]
endif
//store value
set dv[y]=v
//prepare next slot for writing
call open()
debug else
debug if (v<rl[n] or v>rh[n]) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"DATA BUFFER ERROR: VALUE OUT OF BOUNDS\n "+I2S(v)+"-> "+I2S(rl[n])+" ... "+I2S(rh[n]))
debug endif
debug if (df[this]) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"DATA BUFFER ERROR: DATA BUFFER FINALIZED")
debug endif
debug if (not dw[this]) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"DATA BUFFER ERROR: DATA BUFFER NOT OPEN FOR WRITING")
debug endif
debug if (0==de[this]) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"DATA BUFFER ERROR: INVALID ENCODER")
debug endif
debug endif
endmethod
/*************************************************************************************
*
* read
* Reads a value out of the DataBuffer
*
* returns: integer
*
*************************************************************************************/
method read takes nothing returns integer
local Link n
local Link o
//make sure
// buffer isn't finalized
// buffer is open for reading
// buffer is valid
debug if (not dw[this] and 0!=de[this]) then
//retrieve current node
set n=dp[this]
set o=dn[n]
//go to next node
set dp[this]=o
set dz[this]=di[o]
//if no more nodes, deallocate and close
if (o==this) then
set de[this]=0
set dn[dl[this]]=dn[0]
set dn[0]=this
endif
return dv[n]
debug else
debug if (dw[this]) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"DATA BUFFER ERROR: DATA BUFFER NOT OPEN FOR READING")
debug endif
debug if (0==de[this]) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"DATA BUFFER ERROR: INVALID ENCODER")
debug endif
debug endif
return 0
endmethod
/*************************************************************************************
*
* code
* Converts all values in a finalized DataBuffer that is open for writing
* into a save/load code and returns that code.
*
* returns: string
*
*************************************************************************************/
method operator code takes nothing returns string
local BigInt i //code as a number
local integer h //hash
local integer n //node
local string s="" //colorized code string
local string c //character
local boolean l //lowercase
local Base b
//make sure
// buffer is finalized
// buffer is open for writing
// buffer is valid
debug if (df[this] and dw[this] and 0!=de[this]) then
//create the code integer
set i=BigInt.create(b10)
//compress the values into one value
set n=dl[this]
loop
call i.multiply(rsh[dd[n]])
call i.add(dv[n],0)
set n=dl[n]
exitwhen n==this
endloop
//apply checksum
set h=Checksum(i,eh[de[this]][dp[this]])
//if checksum > last value
// range
if (integer(eh[de[this]][dp[this]])>rsh[dd[dl[this]]]) then
//add to front. Have to rebuild the entire number.
call i.destroy()
set i=BigInt.create(b10)
call i.add(h,0)
//compress the values into one value
set n=dl[this]
loop
call i.multiply(rsh[dd[n]])
call i.add(dv[n],0)
set n=dl[n]
exitwhen n==this
endloop
else
//multiply to back
call i.multiply(eh[de[this]][dp[this]])
call i.add(h,0)
endif
//scramble
call ApplyScramble(i,dp[this])
//colorize
set b=eb[de[this]][dp[this]]
set i.base=b
set h=DELIMITER_COUNT
loop
set i=i.previous
exitwhen i.end
set c=b.char(i.digit)
if (0==h) then
set h=DELIMITER_COUNT
set s=s+DELIMITER_COLOR+DELIMITER
endif
set l=StringCase(c,false)==c
if (c==StringCase(c,true) and l) then
if ("0"==c or 0!=S2I(c)) then
//number
set s=s+NUM_COLOR+c
else
//special
set s=s+SPEC_COLOR+c
endif
elseif (l) then
//lower
set s=s+LOWER_COLOR+c
else
//upper
set s=s+UPPER_COLOR+c
endif
set h=h-1
endloop
//close
call i.destroy()
set df[this]=false
set dw[this]=false
set de[this]=0
//deallocate
set dn[dl[this]]=dn[0]
set dn[0]=this
return s+"|r"
debug else
debug if (not df[this]) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"DATA BUFFER ERROR: ATTEMPT TO READ PARTIAL BUFFER")
debug endif
debug if (not dw[this]) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"DATA BUFFER ERROR: DATA BUFFER NOT OPEN FOR WRITING")
debug endif
debug if (0==de[this]) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"DATA BUFFER ERROR: INVALID ENCODER")
debug endif
debug endif
return null
endmethod
endstruct
/*************************************************************************************
*
* Encoder : Link
*
* Used for forming a frame for generating codes
*
* Methods
* static method create takes string base, integer minCodeLength, integer maxCodeLength, integer maxHash, integer ev returns Encoder
* method toString takes nothing returns string
* static method convertString takes string encoderId returns Encoder
* method add takes CodeRange range returns nothing
* method read takes string s, integer p returns DataBuffer
* method write takes integer p returns DataBuffer
*
*************************************************************************************/
struct Encoder extends array
/*************************************************************************************
*
* create
* Creates a new Encoder given a base (characters used in code), a minimum
* code length, a maximum code length, a maximum hash (unique codes),
* and an encoder version.
*
* string b: The haracters used
* Must exist
* integer u: Min code length
* integer x: Max code length
* integer mh: The maximum hash the Encoder can have. Bigger is more unique
* and secure codes, but longer codes.
* Must be > 1
* integer ev: The version of the encoder object
*
* returns: Encoder
*
*************************************************************************************/
static method create takes string b,integer u,integer x,integer mh,integer ev returns thistype
local integer t=StringLength(b) //the Encoder
local string s //Encoder as a string
local string c //Encoder string character
local integer h //string length of Encoder as string
local boolean l //for colorizing (is lower case?)
local integer i=pn[16] //for looping through players
//checksum values
local integer m //min checksum
local integer r //checksum range
//base values
local Base y //original base
local BigInt q //original base int
local BigInt q2 //new base
//first ensure that the base and max checsum are valid
debug if (1<t and 1<mh and x>=u) then
//checksum values
set m=R2I(mh*CHECKSUM_VARIANCE) //min checksum
set r=mh-m //range
//base values
set y=Base[SubString(b,1,2)+SubString(b,0,1)+SubString(b,2,t)] //original base
set q=BigInt.convertString(b,y) //original base int
//instantiate a new Encoder
set t=Link.allocate() //create encoder
set eh[t]=Table.create() //encoder checksum table (different for each player)
set eb[t]=Table.create() //encoder base table (different for each player)
set ml[t]=u //min code length
set mx[t]=x //max code length
//generate checksums and bases for each player
loop
//generate player base
set q2=q.copy() //copy original base
call Scramble(q2,i,3,y,true) //scramble it
set eb[t][i]=Base[q2.toString()] //give to player
call q2.destroy() //clean
//generate player checksum
//checksum=checksum-checksum/checksumRange*checksumRange+minChecksum
set eh[t][i]=ph[i]-ph[i]/r*r+m
set i=pn[i]
exitwhen -1==i
endloop
call q.destroy() //clean original base int
//convert the encoder version** into a string
set ec[ev]=t
set s=es.convertToString(ev)
set h=StringLength(s)
//colorize the string
loop
set h=h-1
set c=SubString(s,h,h+1)
set l=StringCase(c,false)==c
if (c==StringCase(c,true) and l) then
if ("0"==c or 0!=S2I(c)) then
//number
set er[t]=er[t]+NUM_COLOR+c
else
//special
set er[t]=er[t]+SPEC_COLOR+c
endif
elseif (l) then
//lower
set er[t]=er[t]+LOWER_COLOR+c
else
//upper
set er[t]=er[t]+UPPER_COLOR+c
endif
exitwhen 0==h
endloop
return t
debug else
debug if (x<=u) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0,0,60,"ENCODER ERROR: INVALID VALID CODE RANGE")
debug endif
debug if (1>=t) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0,0,60,"ENCODER ERROR: INVALID BASE")
debug endif
debug if (1>=mh) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"ENCODER ERROR: INVALID MAX HASH")
debug endif
debug endif
return 0
endmethod
/*************************************************************************************
*
* toString
* Returns the Encoder as a colorized string in VER_BASE.
*
* returns: string
*
*************************************************************************************/
method toString takes nothing returns string
debug if (er[this] !=null) then
return er[this]
debug endif
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"ENCODER ERROR: INVALID ENCODER")
debug return null
endmethod
/*************************************************************************************
*
* convertSting
* Returns an Encoder by converting an Encoder string into an Encoder.
*
* string s: Encoder id string
*
* returns: Encoder
*
*************************************************************************************/
static method convertString takes string s returns thistype
return ec[es.convertToInteger(s)]
endmethod
/*************************************************************************************
*
* add
* Adds a CodeRange to an Encoder.
*
* CodeRange r: CodeRange to be added
*
* returns: nothing
*
*************************************************************************************/
method add takes CodeRange r returns nothing
debug if (null!=er[this]) then
call Link(this).point(r)
set el[this]=r
debug else
debug if (null==er[this]) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"ENCODER ERROR: INVALID ENCODER")
debug endif
debug endif
endmethod
/*************************************************************************************
*
* read
* Attempts to read values out of a code string.
*
* string s: Code string to read values out of
* integer p: Player id to read code string for
*
* returns: DataBuffer
* 0 means DataBuffer couldn't be openned
*
*************************************************************************************/
method read takes string s,integer p returns DataBuffer
local BigInt i //code integer
local integer k=StringLength(s)
local string c //character
local string b="" //filtered code string
local DataBuffer t //data buffer
local integer v //value
local integer n //node
local integer array o //original values
local boolean f
//first ensure that encoder is valid
if (null!=er[this] and 0!=k) then
//remove all DELIMITERs from code
loop
set k=k-1
set c=SubString(s,k,k+1)
if (c!=DELIMITER) then
set b=c+b
endif
exitwhen 0==k
endloop
set n=StringLength(b)
if (n<ml[this] or n>mx[this]) then
return 0
endif
//convert string into a BigInt
set i=BigInt.convertString(b,eb[this][p])
if (0==i) then
return 0
endif
//unscramble
call UnapplyScramble(i,p)
//retrieve whether the checksum is location at the
//back or the front
// true: front
// false: back
set f=integer(eh[this][p])>rsh[el[this]]
if (not f) then
set i.base=b10
endif
//if the stored checksum wasn't equal to the generated checksum, the
// code isn't valid. This is if the checksum is in the back.
//checksum stored in code //code's actual checksum
if (not f and i.divide(eh[this][p])!=Checksum(i,eh[this][p])) then
call i.destroy()
return 0
endif
//allocate data buffer
if (0==dn[0]) then
set dc=dc+1
set t=dc
else
set t=dn[0]
set dn[0]=dn[t]
endif
set dn[t]=t
set dl[t]=t
//initialize data buffer
set de[t]=this //data buffer encoder
set dm[t]=Link(this).start() //open the loop
loop
//prepare next slot
call t.open()
//exit when there are no slots left (finalized)
exitwhen df[t]
//divide BigInt by shifted high bound of current node of current slot
// this returns the value*
set v=i.divide(rsh[dd[dl[t]]])
//retrieve node
set n=dd[dl[t]]
set o[dl[t]]=v
//shift the value back to what it originally was
if (rf[n]==AN) then
set v=-v+rh[n]
else
set v=v+rl[n]
endif
//store value into depth pointer id
set dm[t].depthPointer.id=v
//store value
set dv[dl[t]]=v
endloop
//unset finalization flag
set df[t]=false
//if the checksum was in the front
if (f) then
//first, retrieve the checksum
set v=i.toInt()
//rebuild the entire number
call i.destroy()
set i=i.create(b10)
set n=dl[t]
loop
call i.multiply(rsh[dd[n]])
call i.add(o[n],0)
set n=dl[n]
exitwhen n==t
endloop
//compare the checksum stored in the number to the checksum
//that the number actually generates
if (v!=Checksum(i,eh[this][p])) then
//if they aren't equal, code wasn't valid
call i.destroy()
set de[t]=0 //data buffer encoder=null
//deallocate data buffer
set dn[dl[t]]=dn[0]
set dn[0]=t
//return null
return 0
endif
endif
call i.destroy()
//code was valid, so return DataBuffer
set dp[t]=dn[t] //current node
set dz[t]=di[dn[t]] //current link id
return t
endif
return 0
endmethod
/*************************************************************************************
*
* write
* Attempts to write value into a code string.
*
* integer p: Player id to write code string for
*
* returns: DataBuffer
* 0 means DataBuffer couldn't be openned
*
*************************************************************************************/
method write takes integer p returns DataBuffer
local integer t
//ensure the player is valid
debug if (0!=ph[p]) then
//ensure the encoder is valid
if (null!=er[this]) then
//allocate data buffer
if (0==dn[0]) then
set dc=dc+1
set t=dc
else
set t=dn[0]
set dn[0]=dn[t]
endif
set dn[t]=t
set dl[t]=t
//initialize the buffer
set dp[t]=p //data buffer player
set de[t]=this //data buffer encoder
set df[t]=false //buffer finalized?
set dw[t]=true //open data buffer for writing
set dm[t]=Link(this).start() //open the loop
//open first buffer slot
call DataBuffer(t).open()
//return the buffer
return t
endif
debug else
debug if (0==ph[p]) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"ENCODER ERROR: INVALID PLAYER")
debug endif
debug endif
//returning 0 means something was wrong
return 0
endmethod
endstruct
/*************************************************************************************
*
* Initialization
*
*************************************************************************************/
private module Init
private static method onInit takes nothing returns nothing
//Retrieve all human player hashes (StringHash player name)
local integer i=11
local player p
set pn[16]=-1
loop
set p=Player(i)
if (GetPlayerSlotState(p)==PLAYER_SLOT_STATE_PLAYING and GetPlayerController(p)==MAP_CONTROL_USER) then
set ph[i]=StringHash(StringCase(GetPlayerName(p)+PLAYER_CHECKSUM_SALT,false))
if (0>ph[i]) then
set ph[i]=-ph[i]
endif
set pn[i]=pn[16]
set pn[16]=i
endif
exitwhen 0==i
set i=i-1
endloop
set p=null
//initialize base 10, binary, and the global encoder base
set b10=Base["0123456789"]
set es=Base[VER_BASE]
endmethod
endmodule
private struct Inits extends array
implement Init
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Scrambler uses /* v3.0.0.2
*************************************************************************************
*
* Scrambles/Shuffles a BigInt given a player id.
*
* This applies a StringHash to the player, converts that hash into base 8, and
* then uses that as a key. The end result is that each player's code (when
* this is used for save/load) is scrambled/shuffled differently.
*
* Scrambling:
* Shuffles in a specific base a number of times.
* Useful for mixing up save/load keys (the character sets)
* Recommend including front digit as data won't be lost for most uses
* Shuffling:
* Shuffles through a set of prime bases a number of times.
* Useful for mixing up save/load codes
*
*************************************************************************************
*
* REQUIREMENTS
*
* */ BigInt /*hiveworkshop.com/forums/jass-functions-413/system-bigint-188973/
*
************************************************************************************
*
* SETTINGS
*/
globals
/*************************************************************************************
*
* SALT
*
* Refers to what to append to the player's name when generating scrambler keys
* for each player. This is essentially like a password. It's impossible for another
* map to descramble a number without this password.
*
* This password can be any string, such as "7/ah+53~r\\ZZ"
*
*************************************************************************************/
private constant string SALT = ""
endglobals
/*************************************************************************************
*
* SetShuffleOrder
*
* Creates the shuffling algorithm using 5 different prime bases.
*
* Shuffling mixes a number up using a variety of bases to produce results rivaling
* top random number generators.
*
* Different bases ensure better mixes.
*
* Enabled Bases:
*
* 2
* Will shuffle number in groups of 1 bit
* 3
* Will shuffle number in groups of 1.58 bits
* 5
* Will shuffle number in groups of 2.32 bits
* 7
* Will shuffle in groups of 2.81 bits
* 11
* Will shuffle in groups of 3.46 bits
*
* Strategies:
*
* 1.
* shuffle by large1, shuffle by small1, shuffle by large2, etc
* 2.
* shuffle by small1, shuffle by large1, shuffle by small2, etc
* 3.
* shuffle by small1, shuffle by small2, shuffle by large1, shuffle by small3, etc
*
* Keep in mind that as fractional bits are shuffled, bits split/merge, meaning that the number
* can drastically change by becoming much smaller or much larger.
*
*
* Shuffle Array: so
*
* Ex:
*
* set so[0]=3 //first mix by 1.58 bits
* set so[1]=2 //second mix by 1 bit
* set so[2]=7 //third mix by 2.81 bits
* set so[3]=2 //fourth mix by 1 bit
*
* return 4 //return number of mixes
*
*************************************************************************************/
private keyword so
private function SetShuffleOrder takes nothing returns integer
/*************************************************************************************
*
* MIXES
*
* array: so
* bases: 2,3,5,7,11
*
*************************************************************************************/
set so[0]=5
set so[1]=2
set so[2]=3
set so[3]=11
set so[4]=2
set so[5]=7
set so[6]=3
/*************************************************************************************
*
* MIX COUNT
*
*************************************************************************************/
return 7
endfunction
/*
************************************************************************************
*
* function Scramble takes BigInt intToScramble, integer forPlayerId, integer shuffles, Base baseToScrambleIn, boolean includeFront returns nothing
* function Unscramble takes BigInt intToScramble, integer forPlayerId, integer shuffles, Base baseToScrambleIn, boolean includeFront returns nothing
*
* function Shuffle takes BigInt intToScramble, integer forPlayerId, integer shuffles returns nothing
* function Unshuffle takes BigInt intToScramble, integer forPlayerId, integer shuffles returns nothing
*
************************************************************************************
*
* function Scramble takes BigInt intToScramble, integer forPlayerId, integer shuffles, Base baseToScrambleIn, boolean includeFront returns nothing
*
* Scrambles a BigInt at the binary level.
*
* intToScramble: BigInt
* forPlayerId: id of human player*
* shuffles: how many times to shuffle number (must be > 0)
* baseToScrambleIn: what base to scramble number in
*
************************************************************************************
*
* function Unscramble takes BigInt intToScramble, integer forPlayerId, integer shuffles, Base baseToScrambleIn, boolean includeFront returns nothing
*
* Unscrambles a BigInt at the binary level.
*
* intToScramble: BigInt
* forPlayerId: id of human player*
* shuffles: how many times to unshuffle number
* baseToScrambleIn: what base to shuffle number in
* includeFront: this determines whether to include front number or not
*
* including the front number isn't advisable as 0s
* may move to the front, meaning data can be lost
*
* Scramble(1000) -> 0001, or 1
*
************************************************************************************
*
* function Shuffle takes BigInt intToScramble, integer forPlayerId, integer shuffles returns nothing
*
* Scrambles a BigInt using user-defined algorithm.
*
* intToScramble: BigInt
* forPlayerId: id of human player*
* shuffles: how many times to shuffle number (must be > 0)
*
************************************************************************************
*
* function Unshuffle takes BigInt intToScramble, integer forPlayerId, integer shuffles returns nothing
*
* Scrambles a BigInt using user-defined algorithm.
*
* intToScramble: BigInt
* forPlayerId: id of human player*
* shuffles: how many times to unshuffle number
*
************************************************************************************/
globals
private integer array ss
private boolean array se
private BigInt array d
private integer i
private integer dc
private integer k
private integer s1
private integer s2
private integer s3
private integer pid
private trigger mt=CreateTrigger() //mix trigger
private trigger dt=CreateTrigger() //demix trigger
private trigger st=CreateTrigger() //scramble trigger
private trigger ut=CreateTrigger() //unscramble trigger
private BigInt bi
private Base array bs
private integer array so
private integer sc=0
endglobals
private function LNF takes BigInt int,boolean i0 returns nothing
set dc=0
if (i0) then
loop
set int=int.next
exitwhen int.end
set d[dc]=int
set dc=dc+1
endloop
else
loop
set int=int.next
exitwhen int.next.end
set d[dc]=int
set dc=dc + 1
endloop
set int=int.next
endif
endfunction
private function LNB takes BigInt int,boolean i0 returns nothing
set dc=0
if (not i0) then
set int=int.previous
endif
loop
set int=int.previous
exitwhen int.end
set d[dc]=int
set dc=dc+1
endloop
endfunction
private function FLP takes integer id,integer i2 returns nothing
//find last position
loop
exitwhen 0==i2
set s1=dc
loop
exitwhen 0==s1
set s1=s1-1
if (se[k]) then
set k=ss[id]
else
set k=k+1
endif
endloop
set i2=i2-1
endloop
endfunction
function Scramble takes BigInt int,integer id,integer shuffles,Base bb,boolean i0 returns nothing
local Base b=int.base
set pid=id
set k=ss[id]
set i=shuffles
debug if (0!=ss[id]) then
if (b!=bb) then
set int.base=bb
endif
//load number
call LNF(int,i0)
//scramble
call TriggerEvaluate(st)
if (b!=bb) then
set int.base=b
endif
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"SCRAMBLER ERROR: INVALID PLAYER "+I2S(id))
debug endif
endfunction
function Unscramble takes BigInt int,integer id,integer shuffles,integer bb,boolean i0 returns nothing
local Base b=int.base
set i=shuffles
set pid=id
set k=ss[id]
debug if (0!=ss[id]) then
if (b!=bb) then
set int.base=bb
endif
//load number
call LNB(int,i0)
//retrieve last position
call FLP(id,shuffles)
//unscramble
call TriggerEvaluate(ut)
if (b!=bb) then
set int.base=b
endif
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"SCRAMBLER ERROR: INVALID PLAYER "+I2S(id))
debug endif
endfunction
//scramble
private function St takes nothing returns boolean
loop
exitwhen 0==i //exitwhen no more shuffles
set s1=dc //loop through digits from left-1 to right
//don't shuffle left most to save a bit as
//left most must be 1
loop
exitwhen 0==s1 //exitwhen no more digits
set s1=s1-1 //shift down as array ends at n-1
//current digit slot - hash digit (shift right)
set s2=s1-ss[k]
//if s2 is negative, add total digits until positive
loop
exitwhen 0<=s2
set s2=dc+s2
endloop
//swap s2 and s1
set s3=d[s2].digit
set d[s2].digit=d[s1].digit
set d[s1].digit=s3
//if out of digits, go back to first digit on hash
//otherwise, go to next digit
//last existing digit is marked as end
if (se[k]) then
set k=ss[pid]
else
set k=k+1
endif
endloop
set i=i-1
endloop
return false
endfunction
//unscramble
private function Ut takes nothing returns boolean
//go backwards
loop
exitwhen 0==i
set s1 = dc
loop
exitwhen 0==s1
set s1=s1-1
set k=k-1
if (0==ss[k]) then
set k=ss[pid+12]
endif
set s2=s1+ss[k]
loop
exitwhen s2<dc
set s2=s2-dc
endloop
set s3=d[s2].digit
set d[s2].digit=d[s1].digit
set d[s1].digit=s3
endloop
set i=i-1
endloop
return false
endfunction
//shuffle
private function Mt takes nothing returns boolean
local integer sh=0
set k=ss[pid]
loop
exitwhen sh==sc
set i=1
set bi.base=bs[so[sh]]
call LNF(bi,false)
call St()
set sh=sh+1
set k=ss[pid]
endloop
return false
endfunction
//unshuffle
private function Dt takes nothing returns boolean
local integer sh=sc
set k=ss[pid]
loop
exitwhen 0==sh
set sh=sh-1
set i=1
set bi.base=bs[so[sh]]
call LNB(bi,false)
call FLP(pid,1)
call Ut()
set k=ss[pid]
endloop
return false
endfunction
function Shuffle takes BigInt int, integer id, integer h returns nothing
local Base b=int.base
debug if (0!=ss[id]) then
set bi=int
set pid=id
loop
exitwhen 0==h
call TriggerEvaluate(mt)
set h=h-1
endloop
set int.base=b
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"SCRAMBLER ERROR: INVALID PLAYER "+I2S(id))
debug endif
endfunction
function Unshuffle takes BigInt int, integer id, integer h returns nothing
local Base b=int.base
debug if (0!=ss[id]) then
set bi=int
set pid=id
loop
exitwhen 0==h
call TriggerEvaluate(dt)
set h=h-1
endloop
set int.base=b
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"SCRAMBLER ERROR: INVALID PLAYER "+I2S(id))
debug endif
endfunction
private module Init
private static method onInit takes nothing returns nothing
local integer is=11
local integer hh
local integer ks=25
local Base b8=Base["012345678"]
local BigInt bg
call TriggerAddCondition(mt,Condition(function Mt))
call TriggerAddCondition(dt,Condition(function Dt))
call TriggerAddCondition(st,Condition(function St))
call TriggerAddCondition(ut,Condition(function Ut))
set bs[2]=Base["01"]
set bs[3]=Base["012"]
set bs[5]=Base["01234"]
set bs[7]=Base["0123456"]
set bs[11]=Base["0123456789A"]
set sc=SetShuffleOrder()
loop
if (GetPlayerSlotState(Player(is))==PLAYER_SLOT_STATE_PLAYING and GetPlayerController(Player(is))==MAP_CONTROL_USER) then
set ss[is]=ks
set hh=StringHash(StringCase(GetPlayerName(Player(is))+SALT,false))
if (0>hh) then
set hh=-hh
endif
set bg=BigInt.create(b8)
call bg.add(hh,0)
set bg=bg.previous
loop
set ss[ks]=bg.digit+1
set bg=bg.previous
exitwhen bg.end
set ks=ks+1
endloop
set se[ks]=true
set ss[is+12]=ks
call bg.destroy()
set ks=ks+2
endif
exitwhen 0==is
set is=is-1
endloop
endmethod
endmodule
private struct Inits extends array
implement Init
endstruct
endlibrary
library KnuthChecksum uses BigInt
function GetKnuthChecksum takes BigInt k, integer m returns integer
local BigInt c = k.copy()
local BigInt c2 = k.copy()
call c.add(3,0)
call c2.multiplyBig(c)
call c.destroy()
set c = c2.mod(m)
call c2.destroy()
return c
endfunction
endlibrary
library WorldBounds
private module WorldBoundInit
private static method onInit takes nothing returns nothing
set world=GetWorldBounds()
set maxX=GetRectMaxX(world)
set maxY=GetRectMaxY(world)
set minX=GetRectMinX(world)
set minY=GetRectMinY(world)
set centerX=(maxX+minX)/2
set centerY=(minY+maxY)/2
set worldRegion=CreateRegion()
call RegionAddRect(worldRegion,world)
endmethod
endmodule
struct WorldBounds extends array
readonly static real maxX
readonly static real maxY
readonly static real minX
readonly static real minY
readonly static real centerX
readonly static real centerY
readonly static rect world
readonly static region worldRegion
implement WorldBoundInit
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Base /* v1.0.3.1
*************************************************************************************
*
* A script used for base conversion where integers are represented as strings in
* another base.
*
*************************************************************************************
*
* */uses/*
*
* */ Ascii /* wc3c.net/showthread.php?t=110153
* */ Table /* hiveworkshop.com/forums/jass-functions-413/snippet-new-table-188084/
*
************************************************************************************
*
* struct Base extends array
*
* static method operator [] takes string base returns Base
*
* method convertToString takes integer i returns string
* method convertToInteger takes string i returns integer
*
* method ord takes string c returns integer
* method char takes integer i returns string
*
* method isValid takes string value returns boolean
* - determines if all of the characters in the string are valid base character
*
*************************************************************************************/
/*************************************************************************************
*
* Code
*
*************************************************************************************/
globals
private Table gt=0 //stacks of strings with same hashes
private integer array n //next node pointer for gt stack
private string array b //base of string
private Table array t //base character table
private integer c=0 //base instance count
private integer array s //base size
endglobals
private module Init
private static method onInit takes nothing returns nothing
set gt=Table.create()
endmethod
endmodule
struct Base extends array
debug private static boolean array a //is allocated
static method operator [] takes string base returns thistype
local integer value //string hash value
local string char //iterated character
local integer i=0 //this
local integer v //stack of hashes
local integer dv //copy of v
debug if (1<StringLength(base)) then
set value = StringHash(base) //first get the hash
set i = gt[value] //get first node of hash table
set v = i //copy
if (0!=i) then //if stack exists, then loop through
loop
exitwhen 0==i or base==b[i]
set i=n[i]
endloop
endif
//if this still doesn't exist, create it
if (0==i) then
//allocate
set c=c+1
set i=c
set dv=v
debug set a[i]=true
set t[i]=Table.create() //character table
set b[i]=base //base string
//value is now used for iterating through the base string
set value=StringLength(base)
set s[i]=value
loop
set value=value-1
set char=SubString(base,value,value+1)
set v=Char2Ascii(char)
//if the character already exists, stop
//and deallocate (invalid base)
debug if (t[i].has(v)) then
debug call t[i].destroy() //destroy character table
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"BASE CREATION ERROR: "+char+" MULTIPLY DEFINED")
debug set c=c-1
debug set a[i]=false
debug return 0
//character doesn't exist
debug else
set t[i][v]=value
set t[i].string[-value]=char
debug endif
exitwhen 0==value
endloop
//if dv is 0, then allocate dv
if (0==dv) then
set gt[value]=i
//otherwise add i to hash stack
else
set n[i]=n[dv]
set n[dv]=i
endif
endif
return i
debug endif
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"BASE CREATION ERROR: "+base+" IS INVALID")
debug return 0
endmethod
method convertToString takes integer i returns string
local integer k=s[this]
local string n=""
debug if (a[this]) then
debug if (0<=i) then
loop
exitwhen i<k
set n=t[this].string[-(i-i/k*k)]+n
set i=i/k
endloop
return t[this].string[-i]+n
debug endif
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"BASE CONVERSION ERROR: "+I2S(i)+" IS OUT OF BOUNDS")
debug return null
debug endif
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"BASE CONVERSION ERROR: "+I2S(this)+" IS NOT ALLOCATED")
debug return null
endmethod
method convertToInteger takes string i returns integer
local integer n=0
local integer p=StringLength(i)
local integer l=0
local integer k=s[this]
local string char
debug if (a[this]) then
loop
exitwhen 0==p
set p=p-1
set l=l+1
set char=SubString(i,l-1,l)
debug if (t[this].has(Char2Ascii(char))) then
set n=n+t[this][Char2Ascii(char)]*R2I(Pow(k,p))
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"BASE CONVERSION ERROR: "+char+" IS OUT OF BOUNDS")
debug return 0
debug endif
endloop
return n
debug endif
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"BASE CONVERSION ERROR: "+I2S(this)+" IS NOT ALLOCATED")
debug return 0
endmethod
method ord takes string c returns integer
debug if (a[this]) then
debug if (1<StringLength(c) or ""==c or null==c or not (t[this].has(Char2Ascii(c)))) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"BASE ORD ERROR: "+c+" IS OUT OF BOUNDS")
debug return 0
debug endif
return t[this][Char2Ascii(c)]
debug endif
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"BASE ORD ERROR: "+I2S(this)+" IS NOT ALLOCATED")
debug return 0
endmethod
method char takes integer i returns string
debug if (a[this]) then
debug if (i<s[this] and 0<=i) then
return t[this].string[-i]
debug endif
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"BASE CHAR ERROR: "+I2S(i)+" IS OUT OF BOUNDS")
debug return null
debug endif
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"BASE CHAR ERROR: "+I2S(this)+" IS NOT ALLOCATED")
debug return null
endmethod
method isValid takes string s returns boolean
local integer i=StringLength(s)
local string c
if (0<i) then
loop
set c=SubString(s,i-1,i)
if (not t[this].has(Char2Ascii(c))) then
return false
endif
set i=i-1
exitwhen 0==i
endloop
else
return false
endif
return true
endmethod
method operator size takes nothing returns integer
return s[this]
endmethod
implement Init
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library QueueQueue /* v2.2.6.5
*******************************************************************
*
* To best understand this data structure, think of a list and then
* imagine that every node on that list can have a list inside of it
* and so on. It's also pretty much a tree.
*
*******************************************************************
*
* API
*
*******************************************************************
*
* module QueueQueue
* readonly thistype parent
* readonly boolean pointer
* boolean skips
*
* static method allocate takes nothing returns thistype
* method deallocate takes nothing returns nothing
*
* method add takes nothing returns thistype
* method point takes integer head returns thistype
* method operator next takes nothing returns thistype
* method operator in takes nothing returns thistype
*
* Loop macros
* //! textmacro QUEUE_QUEUE_HEADER takes STRUCT
* //! textmacro QUEUE_QUEUE_START_LOOP takes THIS
* //! textmacro QUEUE_QUEUE_END_LOOP
* //! textmacro QUEUE_QUEUE_HEADER_B takes STRUCT
* //! textmacro QUEUE_QUEUE_START_LOOP_B takes THIS
* //! textmacro QUEUE_QUEUE_END_LOOP_B
*
* module QueueQueueLoop
* readonly thistype get
* readonly thistype skip
* readonly thistype depthPointer
* readonly thistype depthNode
* integer id
* method start takes nothing returns thistype
* method end takes nothing returns nothing
*
*******************************************************************
*
* module QueueQueue
*
*******************************************************************
*
* readonly thistype next
* - Next node
* readonly thistype in
* - Inner node (any given node can have nodes inside of it)
* readonly thistype parent
* - The node that the node is contained inside of.
* readonly boolean pointer
* - This determines whether the node is a pointer or not. Pointers are
* - the first node on the QueueQueue (a special pointer called a head, only
* - generated with allocate) and nodes made through the point method. Pointers
* - are automatically skipped, however their skip option can be set with the
* - skips property.
* boolean skips
* - This determines whether or not to skip a pointer. Pointers that are skipped
* - are not looped over in loops. This is useful for systems that want to read
* - values inside of pointers to see if a pointer's contents should be skipped
* - over or not.
*
* static method allocate takes nothing returns thistype
* - Returns the head for the entire object.
* method deallocate takes nothing returns nothing
* - Destroys the entire structure given the head
*
* method add takes nothing returns thistype
* - Adds a cell to the cell. Cells generated in this fashion
* - are entirely local to the QueueQueue they were added to, meaninng
* - that they can't be pointed to.
* method point takes integer head returns thistype
* - Points a node to a head. Any node allocated with the allocate
* - method is a head. Pointers can't point to anything other than the head.
*
********************************************************************
*
* Loop macros
*
* A loop macro used for looping through a QueueQueue.
*
*******************************************************************
*
* Regular loop (looped in right order)
* //! textmacro QUEUE_QUEUE_HEADER takes STRUCT
* - Place at top of the method/function.
* -
* - STRUCT: Refers to the struct type to loop for.
* //! textmacro QUEUE_QUEUE_START_LOOP takes THIS
* - Place where the loop should start
* -
* - THIS: Refers to where to start the loop from and also
* - used to store current node inside of for reading.
* - THIS must be a variable, and it is not recommended
* - that the this variable be used.
* //! textmacro QUEUE_QUEUE_END_LOOP
* - Place where the loop should end
*
* Deep first postorder loop macros (inner before outer, backwards)
* //! textmacro QUEUE_QUEUE_HEADER_B takes STRUCT
* //! textmacro QUEUE_QUEUE_START_LOOP_B takes THIS
* //! textmacro QUEUE_QUEUE_END_LOOP_B
*
* ---------------------------------------------------------------------
* -
* - Example
* -
* - //both are used the same way
* - function Test takes QueueQueue q returns nothing
* - //header
* - //! runtextmacro QUEUE_QUEUE_HEADER("QueueQueue")
* -
* - local QueueQueue node = q //make a copy
* -
* - //loop
* - //! runtextmacro QUEUE_QUEUE_START_LOOP("node")
* - //code
* - call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, I2S(node))
* - //endloop
* - //! runtextmacro QUEUE_QUEUE_END_LOOP
* - endfunction
* -
* ---------------------------------------------------------------------
*
*********************************************************************
*
* module QueueQueueLoop
*
* A module that provides methods for looping through a
* QueueQueue given an instanced looper.
*
*******************************************************************
*
* readonly thistype get
* - Retrieves the next value in the QueueQueue given a looper.
* readonly thistype skip
* - Skips inner values of the current node and returns next node.
* readonly thistype depthPointer
* - Returns pointer of current depth of loop that stores node. Depth
* - pointers are a stack, so depthPointer.depthPointer would go up one.
* readonly thistype depthNode
* - Returns the node stored within the depth pointer.
* integer id
* - For referencing a depth pointer to something. Depth pointers can be recycled
* - en masse w/o the user's knowledge, meaning that depth pointers should not be used
* - as references for values. It is best to reference the id of a depth pointer.
*
* method start takes nothing returns thistype
* - Creates a new looper for the QueueQueue.
* method end takes nothing returns nothing
* - Destroys a looper for a QueueQueue. Only used when not looping
* - through the entire QueueQueue. The looper is automatically destroyed
* - if the entire QueueQueue is looped through. This could be useful for value
* - searches where looping through the rest of the QueueQueue is pointles.
*
* ---------------------------------------------------------------------
* -
* - Example
* -
* - function Test takes QueueQueue q returns nothing
* - local QueueQueue looper = q.start() //create a new looper
* - local QueueQueue node
* -
* - loop
* - set node = looper.get //get the next node
* - exitwhen node == 0 //exitwhen the node is null
* -
* - //code
* - call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, I2S(node))
* - endloop
* - endfunction
* -
* ---------------------------------------------------------------------
*
*******************************************************************/
module QueueQueue
private static integer array nc //next column
private static integer array nr //next row
private static integer array lr //last row
private static integer ic=0 //column count
private static integer array rc //position recycler
debug private static boolean array a //is node allocated
debug private static integer array rs //references
private static boolean array h
readonly boolean pointer
readonly thistype parent
private boolean z
method operator skips takes nothing returns boolean
return z
endmethod
method operator skips= takes boolean b returns nothing
debug if (pointer) then
set z=b
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"QUEUE QUEUE ERROR: ATTEMPTED TO MANIPULATE SKIP OPTION FOR NON POINTER")
debug endif
endmethod
//can only add rows to current object
static method allocate takes nothing returns thistype
local thistype this
//allocate
if (0==rc[0]) then
set this=ic+1
set ic=this
else
set this=rc[0]
set rc[0]=rc[this]
endif
debug set a[this]=true
set nc[this]=0 //next column is 0
set nr[this]=0 //next row is 0
set pointer=true //is a pointer
set z=true
set parent=0
//is head
debug set h[this]=true
return this
endmethod
method add takes nothing returns thistype
local thistype n
debug if (a[this] and (not pointer or h[this])) then
//allocate
if (0==rc[0]) then
set n=ic+1
set ic=n
else
set n=rc[0]
set rc[0]=rc[n]
endif
debug set a[n]=true
if (0==nc[this]) then
//if nothing to the right, expand
set nc[this]=n
set lr[this]=n
else
//otherwise, add
set nr[lr[this]]=n
set lr[this]=n
endif
set nr[n]=0 //next row is 0
set nc[n]=0 //next column is 0
set n.parent=this
return n
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"QUEUE QUEUE ERROR: ATTEMPTED TO MANIPULATE NULL NODE")
debug endif
debug return 0
endmethod
//special add that adds an existing cell
method point takes integer p returns thistype
local thistype n
//if allocated and not a shadowed pointer (hidden)
debug if (a[this] and (not pointer or h[this]) and h[p]) then
//allocate
if (0==rc[0]) then
set n=ic+1
set ic=n
else
set n=rc[0]
set rc[0]=rc[n]
endif
debug set a[n]=true
//add
if (0==nc[this]) then
//if nothing to the right, expand
set nc[this]=n
set lr[this]=n
else
//otherwise, add
set nr[lr[this]] = n
set lr[this] = n
endif
//expand and point to p
set nc[n]=p
set nr[n]=0
set n.pointer=true
set n.z=true
set n.parent=this
debug if (h[p]) then
debug set rs[p]=rs[p]+1
debug endif
return n
debug else
debug if (not h[p]) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"QUEUE QUEUE ERROR: ATTEMPTED TO POINT TO NON HEAD")
debug elseif (a[this]) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"QUEUE QUEUE ERROR: ATTEMPTED TO MANIPULATE NON HEAD POINTER")
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"QUEUE QUEUE ERROR: ATTEMPTED TO MANIPULATE NULL NODE")
debug endif
debug endif
return 0
endmethod
method operator next takes nothing returns thistype
return nr[this]
endmethod
method operator in takes nothing returns thistype
return nc[this]
endmethod
method deallocate takes nothing returns nothing
local thistype array r //first row on column
local thistype c = -1 //column
local thistype n = this //node
local boolean p = false
debug if (h[n] and 0==rs[n]) then
debug set h[n]=false
debug set a[n]=false
set rc[n]=rc[0]
set rc[0]=n
set pointer=false
set z=false
loop
if (p) then
debug if (h[nc[n]]) then
debug set rs[nc[n]]=rs[nc[n]]-1
debug endif
set n.pointer=false
set n.z=false
loop
//go left until can go down or all the way left
exitwhen 0!=nr[n] or 0==c
set c=c-1
set n=r[c]
endloop
set n=nr[n]
set r[c]=n
else
if (0==nc[n]) then
//if there is nothing to the right, then go down
loop
//go left until can go down or all the way left
exitwhen 0!=nr[n] or 0==c
set c=c-1
set n=r[c]
endloop
set n=nr[n]
set r[c]=n
else
//go right
set c=c+1 //go to next column
set n=nc[n]
set r[c]=n //update next column
endif
endif
exitwhen 0==n
set p=n.pointer
debug set a[n]=false
//destroy
set rc[n]=rc[0]
set rc[0]=n
endloop
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"QUEUE QUEUE ERROR: ATTEMPT TO DESTROY NON HEAD "+I2S(n))
debug endif
endmethod
endmodule
//! textmacro QUEUE_QUEUE_HEADER_B takes STRUCT
local boolean array d_1
local $STRUCT$ array r_1
local $STRUCT$ c_1
local $STRUCT$ n_1
//! endtextmacro
//! textmacro QUEUE_QUEUE_START_LOOP_B takes THIS
set n_1=$THIS$
set c_1=0
if (0!=n_1.in) then
loop
//go to lowest position
loop
exitwhen d_1[c_1] or (0==n_1.next and 0==n_1.in)
set d_1[c_1]=true
//first go down, then right
if (0!=n_1.in) then
set c_1=c_1+1
set r_1[c_1]=n_1
if (0!=n_1.next) then
set c_1=c_1+1
set r_1[c_1]=n_1.in
set n_1=n_1.next
else
set n_1=n_1.in
endif
elseif (0!=n_1.next) then
set c_1=c_1+1
set r_1[c_1]=n_1
set n_1=n_1.next
endif
endloop
if (not n_1.skips) then
set $THIS$=n_1
//! endtextmacro
//! textmacro QUEUE_QUEUE_END_LOOP_B
endif
//go up 1
set d_1[c_1]=false
set n_1=r_1[c_1]
set c_1=c_1-1
exitwhen 0==n_1
endloop
endif
//! endtextmacro
//! textmacro QUEUE_QUEUE_HEADER takes STRUCT
local $STRUCT$ array r_1
local $STRUCT$ c_1
local $STRUCT$ n_1
//! endtextmacro
//! textmacro QUEUE_QUEUE_START_LOOP takes THIS
set n_1=$THIS$
set c_1=-1
loop
if (not n_1.skips) then
set $THIS$=n_1
//! endtextmacro
//! textmacro QUEUE_QUEUE_END_LOOP
endif
if (0==n_1.in) then
//if there is nothing to the right, then go down
loop
//go left until can go down or all the way left
exitwhen 0!=n_1.next or 0==c_1
set c_1=c_1-1
set n_1=r_1[c_1]
endloop
set n_1=n_1.next
set r_1[c_1]=n_1
else
//go right
set c_1=c_1+1 //go to next column
set n_1=n_1.in
set r_1[c_1]=n_1 //update next column
endif
exitwhen 0==n_1
endloop
//! endtextmacro
module QueueQueueLoop
private thistype c
private thistype n
private boolean e
private boolean s
integer id
debug private boolean o
private static thistype array rr //recyler
private static thistype rc=0 //instance count
method operator depthNode takes nothing returns thistype
return n
endmethod
method operator depthPointer takes nothing returns thistype
local thistype h=c
loop
exitwhen not h.n.skips
set h=h.c
endloop
if (h==this) then
return 0
endif
return h
endmethod
method operator skip takes nothing returns thistype
local thistype n=c.n
debug if (o) then
//first, skip the node
loop
//go left until can go down or all the way left
exitwhen 0!=n.next or c.e
//destroy
set rr[c]=rr[0]
set rr[0]=c
set c=c.c
set n=c.n
endloop
set n=n.next
set c.n=n
//then find a node that doesn't skip
loop
exitwhen not n.skips
if (0==n.in) then
//if there is nothing to the right, then go down
loop
//go left until can go down or all the way left
exitwhen 0!=n.next or c.e
//destroy
set rr[c]=rr[0]
set rr[0]=c
set c=c.c
set n=c.n
endloop
set n=n.next
set c.n=n
else
//go right
if (0==rr[0]) then
set rc=rc+1
set rc.c=c
set c=rc
else
set rr[0].c=c
set c=rr[0]
set rr[0]=rr[c]
endif
set c.id=0
set n=n.in
set c.n=n
endif
endloop
if (0==n) then
debug set o=false
set s=false
set e=false
set rr[this]=rr[0]
set rr[0]=this
else
set c.id=0
endif
return n
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"QUEUE QUEUE LOOP ERROR: LOOP NOT OPEN")
debug endif
debug return 0
endmethod
method operator get takes nothing returns thistype
local thistype n=c.n
local string es=""
debug if (o) then
if (s) then
set s=false
if (not n.skips) then
return n
endif
endif
loop
if (0==n.in) then
//if there is nothing to the right, then go down
loop
//go left until can go down or all the way left
exitwhen 0!=n.next or c.e
//destroy
set rr[c]=rr[0]
set rr[0]=c
set c=c.c
set n=c.n
endloop
set n=n.next
set c.n=n
else
//go right
if (0==rr[0]) then
set rc=rc+1
set rc.c=c
set c=rc
else
set rr[0].c=c
set c=rr[0]
set rr[0]=rr[c]
endif
set c.id=0
set n=n.in
set c.n=n
endif
exitwhen not n.skips
endloop
if (0==n) then
debug set o=false
set s=false
set e=false
set rr[this]=rr[0]
set rr[0]=this
else
set c.id=0
endif
return n
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"QUEUE QUEUE LOOP ERROR: LOOP NOT OPEN")
debug endif
debug return 0
endmethod
method start takes nothing returns thistype
local thistype t
if (0==rr[0]) then
set rc=rc+1
set t=rc
else
set t=rr[0]
set rr[0]=rr[t]
endif
set t.id=0
set t.s=true
set t.c=t
set t.e=true
set t.n=this
debug set t.o=true
return t
endmethod
method end takes nothing returns nothing
local thistype c=this.c
debug if (o) then
debug set o=false
loop
exitwhen c.e
set rr[c]=rr[0]
set rr[0]=c
set c=c.c
endloop
set s=false
set e=false
set rr[c]=rr[0]
set rr[0]=c
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"QUEUE QUEUE LOOP ERROR: LOOP NOT OPEN")
debug endif
endmethod
endmodule
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Catalog /* v1.1.0.3
*************************************************************************************
*
* A system used to generate catalogs of objects by hashing raw code ids into indexed
* ids. Catalogs can be used in save/load systems to save things like units and items.
*
*************************************************************************************
* */uses/*
*
* */ Table /* hiveworkshop.com/forums/jass-functions-413/snippet-new-table-188084/
*
************************************************************************************
*
* Functions
*
* function CatalogCreate takes nothing returns integer
* - Creates a new Catalog
* function CatalogAdd takes integer catalog, integer value returns nothing
* - Adds a value to the catalog
* function CatalogAddCatalog takes integer catalog, integer catalogToAdd returns nothing
* - Adds a catalog to the catalog
* function CatalogId takes integer catalog, integer value returns integer
* - Retrieves catalog id of raw value
* function CatalogRaw takes integer catalog, integer value returns integer
* - Retrieves the raw value given catalog id
* function CatalogCount takes integer catalog returns integer
* - Retrieves total items in catalog (includes added catalogs)
*
************************************************************************************
*
* module Catalog
*
* - Implements a catalog into the struct
*
* readonly static thistype catalog
* - Retrieves the instance id of the catalog. Used for adding it to other
* - catalogs.
* readonly static integer count
* - Retrieves the total amount of values inside of the catalog. Includes totals
* - of added catalogs.
* readonly integer raw
* - Gets the raw value given a catalog value. Raw values are the
* - original values that were added.
*
* - Ex: integer raw = Catalog[1].raw
* readonly integer id
* - Gets the catalog id given a raw value
*
* - Ex: integer id = Catalog['hpea'].id
*
* static method add takes integer value returns nothing
* - Adds a value to the catalog. Values already inside of the catalog
* - or inside of any catalog that it contains are not added.
* static method addCatalog takes integer catalog returns nothing
* - Adds a catalog and all of its inner catalogs to the catalog.
* - Catalogs already inside of the catalog are not added.
*
***********************************************************************************/
globals
private integer w=0 //catalog instance count
private integer array c //catalog count
private Table array i //id given rawcode
private Table array r //rawcode given id
private Table array f //has catalog
private integer array l //last
private integer array e //extends
private integer array p //pointer
endglobals
function CatalogCreate takes nothing returns integer
set w=w+1
set l[w]=w
set p[w]=w
set i[w]=Table.create()
set r[w]=Table.create()
set f[w]=Table.create()
return w
endfunction
function CatalogCount takes integer b returns integer
local integer l=c[b]
loop
set b=e[b]
exitwhen 0==b
set l=l+c[p[b]]
endloop
return l
endfunction
function CatalogRaw takes integer b,integer v returns integer
local integer y
loop
set y=c[p[b]]
exitwhen v<=y
set v=v-y
set b=e[b]
if (0==b) then
return 0
endif
endloop
return r[p[b]][v]
endfunction
function CatalogId takes integer b,integer v returns integer
local integer l=0
loop
exitwhen i[p[b]].has(v)
set l=l+c[p[b]]
set b=e[b]
if (0==b) then
return 0
endif
endloop
return i[p[b]][v]+l
endfunction
function CatalogAdd takes integer y,integer v returns nothing
if (0==CatalogId(y, v)) then
set c[y]=c[y]+1
set i[y][v]=c[y]
set r[y][c[y]]=v
endif
endfunction
function CatalogAddCatalog takes integer y,integer b returns nothing
local integer z=w
loop
if (not f[y].boolean.has(p[b])) then
set z=z+1
set p[z]=p[b]
set f[y].boolean[p[b]]=true
set e[l[y]]=z
set l[y]=z
endif
set b=e[b]
exitwhen 0==b
endloop
set w=z
endfunction
module Catalog
readonly static thistype catalog //catalog instance
method operator raw takes nothing returns integer
return CatalogRaw(catalog,this)
endmethod
static method operator count takes nothing returns integer
return CatalogCount(catalog)
endmethod
method operator id takes nothing returns integer
return CatalogId(catalog,this)
endmethod
static method add takes integer v returns nothing
call CatalogAdd(catalog, v)
endmethod
static method addCatalog takes integer catalog returns nothing
call CatalogAddCatalog(thistype.catalog,catalog)
endmethod
private static method onInit takes nothing returns nothing
set catalog=CatalogCreate()
endmethod
endmodule
endlibrary
//TESH.scrollpos=39
//TESH.alwaysfold=0
library Ascii
///////////////////////////////////////////////////////////////////
// function Char2Ascii takes string s returns integer
// integer ascii = Char2Ascii("F")
//
// function Ascii2Char takes integer a returns string
// string char = Ascii2Char('F')
//
// function A2S takes integer a returns string
// string rawcode = A2S('CODE')
//
// function S2A takes string s returns integer
// integer rawcode = S2A("CODE")
//
///////////////////////////////////////////////////////////////////
globals
private integer array i //hash
private string array c //char
endglobals
function Char2Ascii takes string s returns integer
local integer a
if ("\\"==s) then
return 92
endif
set a=i[StringHash(s)/0x1F0748+0x3EA]
if (s!=c[a]) then
debug if (0==a) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"ASCII ERROR: INVALID CHARACTER")
debug return 0
debug endif
return a+32
endif
return a
endfunction
function Ascii2Char takes integer a returns string
return c[a]
endfunction
function A2S takes integer a returns string
local string s=""
loop
set s=c[a-a/256*256]+s
set a=a/256
exitwhen 0==a
endloop
return s
endfunction
function S2A takes string s returns integer
local integer a=0
local integer l=StringLength(s)
local integer j=0
local string m
local integer h
loop
exitwhen j==l
set m=SubString(s,j,j+1)
if ("\\"==m) then
set a=a*256+92
else
set h=i[StringHash(m)/0x1F0748+0x3EA]
if (m!=c[h]) then
debug if (0==a) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"ASCII ERROR: INVALID CHARACTER")
debug return 0
debug endif
set a=a*256+h+32
else
set a=a*256+h
endif
endif
set j=j+1
endloop
return a
endfunction
private module Init
private static method onInit takes nothing returns nothing
set i[931]=8
set i[1075]=9
set i[1586]=10
set i[1340]=12
set i[412]=13
set i[198]=32
set i[1979]=33
set i[1313]=34
set i[1003]=35
set i[1264]=36
set i[983]=37
set i[1277]=38
set i[306]=39
set i[904]=40
set i[934]=41
set i[917]=42
set i[1972]=43
set i[1380]=44
set i[1985]=45
set i[869]=46
set i[1906]=47
set i[883]=48
set i[1558]=49
set i[684]=50
set i[582]=51
set i[668]=52
set i[538]=53
set i[672]=54
set i[1173]=55
set i[71]=56
set i[277]=57
set i[89]=58
set i[1141]=59
set i[39]=60
set i[1171]=61
set i[51]=62
set i[305]=63
set i[0]=64
set i[222]=65
set i[178]=66
set i[236] =67
set i[184]=68
set i[1295]=69
set i[1390]=70
set i[1276]=71
set i[203]=72
set i[1314]=73
set i[209]=74
set i[1315]=75
set i[170]=76
set i[1357]=77
set i[1343]=78
set i[1397]=79
set i[1420]=80
set i[1419]=81
set i[1396]=82
set i[1374]=83
set i[1407]=84
set i[499]=85
set i[1465]=86
set i[736]=87
set i[289]=88
set i[986]=89
set i[38]=90
set i[1230]=91
set i[1636]=93
set i[1416]=94
set i[1917]=95
set i[217]=96
set i[833]=123
set i[1219]=124
set i[553]=125
set i[58]=126
set c[8]="\b"
set c[9]="\t"
set c[10]="\n"
set c[12]="\f"
set c[13]="\r"
set c[32]=" "
set c[33]="!"
set c[34]="\""
set c[35]="#"
set c[36]="$"
set c[37]="%"
set c[38]="&"
set c[39]="'"
set c[40]="("
set c[41]=")"
set c[42]="*"
set c[43]="+"
set c[44]=","
set c[45]="-"
set c[46]="."
set c[47]="/"
set c[48]="0"
set c[49]="1"
set c[50]="2"
set c[51]="3"
set c[52]="4"
set c[53]="5"
set c[54]="6"
set c[55]="7"
set c[56]="8"
set c[57]="9"
set c[58]=":"
set c[59]=";"
set c[60]="<"
set c[61]="="
set c[62]=">"
set c[63]="?"
set c[64]="@"
set c[65]="A"
set c[66]="B"
set c[67]="C"
set c[68]="D"
set c[69]="E"
set c[70]="F"
set c[71]="G"
set c[72]="H"
set c[73]="I"
set c[74]="J"
set c[75]="K"
set c[76]="L"
set c[77]="M"
set c[78]="N"
set c[79]="O"
set c[80]="P"
set c[81]="Q"
set c[82]="R"
set c[83]="S"
set c[84]="T"
set c[85]="U"
set c[86]="V"
set c[87]="W"
set c[88]="X"
set c[89]="Y"
set c[90]="Z"
set c[92]="\\"
set c[97]="a"
set c[98]="b"
set c[99]="c"
set c[100]="d"
set c[101]="e"
set c[102]="f"
set c[103]="g"
set c[104]="h"
set c[105]="i"
set c[106]="j"
set c[107]="k"
set c[108]="l"
set c[109]="m"
set c[110]="n"
set c[111]="o"
set c[112]="p"
set c[113]="q"
set c[114]="r"
set c[115]="s"
set c[116]="t"
set c[117]="u"
set c[118]="v"
set c[119]="w"
set c[120]="x"
set c[121]="y"
set c[122]="z"
set c[91]="["
set c[93]="]"
set c[94]="^"
set c[95]="_"
set c[96]="`"
set c[123]="{"
set c[124]="|"
set c[125]="}"
set c[126]="~"
endmethod
endmodule
private struct Inits extends array
implement Init
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library BigInt /* v1.1.4.7
*************************************************************************************
*
* Used for creating very large integers in any base. Stores in linked list.
*
*************************************************************************************
*
* */uses/*
*
* */ Base /* hiveworkshop.com/forums/submissions-414/snippet-base-188814/
*
*************************************************************************************
*
* struct BigInt extends array
*
* Base base
* - The current base of the integer
* - Changing this value will convert the BigInt value into another base
*
* integer digit
* - Specific digit within BigInt
* readonly BigInt next
* - Used for iterating over digits
* - Goes from first placehold to last
* - Looping through 10 would be 0 to 1
* readonly BigInt previous
* - Used for iterating over digits
* - Goes from last placeholder to first
* - Looping through 10 would be 1 to 0
* readonly boolean end
* - The head, the pointer that represents the BigInt
* - Does not store a value, exit loop when this is true
*
* static method create takes Base base returns BigInt
* - Creates a new BigInt in a base
* - Semi small operation
* method destroy takes nothing returns nothing
* - Destroys a BigInt. Must pass in the integer pointer or this
* - will bug the rest of the system.
* - Very small operation.
*
* method add takes integer value, integer extraPower returns nothing
* - Integer will be added as base 10 integers, meaning that they will
* - be converted into the base. If wanting to add integers in the same
* - base, convert it before hand. 0x and 0 notation for hexadecimal
* - and octal are very useful as well as they work with the integers
* - in an already converted state.
* -
* - value and extraPower must be greater than 0
* -
* - extraPower refers to value^(1+extraPower)
* method multiply takes integer value returns nothing
* - Multiplies an integer. Value is treated as a base 10 integer.
* method divide takes integer value returns integer
* - Divides an integer by an integer. Value is treated as a base10 integer.
* -
* - Returns remainder
* method mod takes integer k returns integer
* - Returns the modulo of a BigInt given k as a divisor.
*
* method addBig takes BigInt integerToAdd, integer extraPower returns nothing
* - Adds BigInt integerToAdd to BigInt
* method multiplyBig takes BigInt integerToAdd returns nothing
* - Multiplies BigInt by BigInt integerToAdd
*
* method push takes integer value returns nothing
* - pushes a base 10 integer on to the right side of BigInt
* - and converts that base 10 number into the BigInt's base
* method pop takes nothing returns integer
* - pop does the reverse of push and returns the popped digit
* - (see pop @google)
* method enq takes integer value returns nothing
* - pushes base 10 integer to the left side of the number
* method deq takes nothing returns integer
* - deq does the reverse of enq and returns popped digit
* method rl takes nothing returns nothing
* - Rotates number left
* method rr takes nothing returns nothing
* - Rotates number right
*
* method copy takes nothing returns BigInt
* - Returns a copy of the BigInt
*
* method toString takes nothing returns string
* - converts an integer into a string (extremely useful)
* -
* - if working in hexadecimal and there was a value like
* - C1H4, it'd actually return "C1H4"
* -
* - string conversions use the actual Base to perform the
* - character conversions
* method toInt takes nothing returns integer
* - returns the value stored in the BigInt as an integer.
* - only use when the value can be stored within an integer.
* - one common use might be retrieving the last value in a save/load
* - code.
* static method convertString takes string value, Base base returns BigInt
* - converts a string into an BigInt
* -
* - if working with hexadecimal and inputted "C1H4", it would
* - return an BigInt containing C1H4.
* -
* - Returns 0 if the string value contains invalid characters
*
*************************************************************************************
*
* Error Messages
*
* Thread crash error messages output the trigger that crashed.
* Feel free to post a message up for the resource about the crashed trigger
* including the trigger name.
*
* To find the specific method that crashed, do CTRL+F trigger name two times
* (should be at the onInit method). From there, look at the method that's
* added to the trigger and then CTRL+F that method.
*
* Finding t-
* private trigger t = CreateTrigger()
* call TriggerAddCondition(t, Condition(function thistype.cl))
* private static method cl takes nothing returns boolean
*
* Overflow errors have to do with too many allocated digits. If running into these, the numbers
* are just too large. This has been tested with 61*62^120 (base 62 number) converted to binary and
* back, so it can handle extremely large digits (binary number was over 700 digits)
*
*************************************************************************************/
/************************************************************************************
*
* Code
*
* if you want to read the code, my suggestion is to not do it
* this system performs operations digit by digit, meaning the algorithms
* are ugly
*
* the code is optimized, meaning the variable names aren't descriptive
* the point is that it works and it's optimization is based on a balance
* between minimizing counts towards the op limit and actual speed
*
************************************************************************************/
globals
private integer i=0 //instance count
private integer array n //next
private integer array p //previous
private integer array v //value
private Base array b //base of number
private boolean array e //is last
//bundle of random triggers and trigger args
private trigger j=CreateTrigger()
private trigger x=CreateTrigger()
private trigger x4=CreateTrigger()
private trigger x5=CreateTrigger()
private trigger y0=CreateTrigger()
private trigger y6=CreateTrigger()
private trigger y7=CreateTrigger()
private trigger y8=CreateTrigger()
private trigger y9=CreateTrigger()
private trigger x1=CreateTrigger()
private trigger x2=CreateTrigger()
private integer td=0
private integer my=0
private integer my3=0
private integer td2=0
private string yy=""
private integer dz
endglobals
private module IntMod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(j,Condition(function thistype.ch))
call TriggerAddCondition(x,Condition(function thistype.ml))
call TriggerAddCondition(x4,Condition(function thistype.div))
call TriggerAddCondition(x5,Condition(function thistype.s2))
call TriggerAddCondition(y0,Condition(function thistype.a0))
call TriggerAddCondition(y7,Condition(function thistype.m0))
call TriggerAddCondition(y6,Condition(function thistype.tos))
call TriggerAddCondition(y8,Condition(function thistype.c0))
call TriggerAddCondition(y9,Condition(function thistype.a1))
call TriggerAddCondition(x1,Condition(function thistype.m1))
call TriggerAddCondition(x2,Condition(function thistype.m2))
endmethod
endmodule
struct BigInt extends array
//useful for loading into a buffer
method operator previous takes nothing returns thistype
return p[this]
endmethod
method operator next takes nothing returns thistype
return n[this]
endmethod
method operator digit takes nothing returns integer
return v[this]
endmethod
method operator digit= takes integer y returns nothing
set v[this] = y
endmethod
method operator end takes nothing returns boolean
return e[this]
endmethod
//copy
private static method c0 takes nothing returns boolean
local integer y
local integer z
local integer q
local integer k=td
if (0==n[0]) then
set i=i+1
set z=i
else
set z=n[0]
set n[0]=n[z]
endif
set e[z]=true
set b[z]=b[k]
set v[z]=0
set q=z
loop
set k=n[k]
exitwhen e[k]
if (0==n[0]) then
set i=i+1
set y=i
else
set y=n[0]
set n[0]=n[y]
endif
set n[q]=y
set p[y]=q
set v[y]=v[k]
set q=y
endloop
set p[z]=q
set n[q]=z
set dz=z
return true
endmethod
method copy takes nothing returns thistype
set td=this
//call c0
static if DEBUG_MODE then
if (not TriggerEvaluate(y8)) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"THREAD CRASH BIG_INT ERROR: y8")
endif
else
call TriggerEvaluate(y8)
endif
return dz
endmethod
//addBig
private static method a1 takes nothing returns boolean
local integer y=td
local integer a
local integer z=b[y].size
local integer q=0
local integer m
local integer g=dz
local integer k=my
loop
if (e[n[y]]) then
if (0==n[0]) then
set i=i+1
set a=i
else
set a=n[0]
set n[0]=n[a]
endif
set p[a]=y
set n[a]=td
set n[y]=a
set p[td]=a
set v[a]=0
endif
set y=n[y]
exitwhen 0==g
set g=g-1
endloop
loop
set k=n[k]
set v[y]=v[y]+v[k]
set m=y
loop
exitwhen z>v[m]
set q=v[m]/z
set v[m]=v[m]-v[m]/z*z
if (e[n[m]]) then
if (0==n[0]) then
set i=i+1
set a=i
else
set a=n[0]
set n[0]=n[a]
endif
set p[a]=m
set n[a]=td
set n[m]=a
set p[td]=a
set v[a]=0
endif
set m=n[m]
set v[m]=v[m]+q
endloop
exitwhen e[n[k]]
if (e[n[y]]) then
if (0==n[0]) then
set i=i+1
set a=i
else
set a=n[0]
set n[0]=n[a]
endif
set p[a]=y
set n[a]=td
set n[y]=a
set p[td]=a
set v[a]=0
endif
set y=n[y]
endloop
return true
endmethod
method addBig takes BigInt k,integer g returns nothing
debug if (0<=g and b[k].size==b[this].size) then
if (0<v[p[k]]) then
set td=this
set my=k
set dz=g
//call a1
static if DEBUG_MODE then
if (not TriggerEvaluate(y9)) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"THREAD CRASH BIG_INT ERROR: y9")
endif
else
call TriggerEvaluate(y9)
endif
endif
debug else
debug if (b[k].size!=b[this].size) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"BIG_INT ERROR: "+I2S(this)+" base not equal to "+I2S(k))
debug endif
debug if (0>g) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"BIG_INT ERROR: "+I2S(this)+" attempt to add -power "+I2S(g))
debug endif
debug endif
endmethod
private static integer array zs
//multiplyBig
private static method m2 takes nothing returns boolean
local integer q=td2
local integer m=dz
local integer a
local integer k=my3
loop
set q=q-1
if (e[n[m]]) then
if (0==n[0]) then
set i=i+1
set a=i
else
set a=n[0]
set n[0]=n[a]
endif
set p[a]=m
set n[a]=td
set n[m]=a
set p[td]=a
set v[a]=0
endif
set m=n[m]
set v[m]=v[m]+zs[q]*v[k]
exitwhen 0==q
endloop
return true
endmethod
//multiplyBig
private static method m1 takes nothing returns boolean
local integer y=0
local integer q=0
local integer a=td
local integer m=a
local integer w=a
local integer k=my
loop
set a=p[a]
exitwhen e[a]
set zs[y]=v[a]
set y=y+1
endloop
set n[p[a]]=n[0]
set n[0]=n[a]
set n[a]=a
set p[a]=a
loop
set k=n[k]
exitwhen e[k]
set td2=y
set w=n[w]
set dz=w
set my3=k
//call m2
static if DEBUG_MODE then
if (not TriggerEvaluate(x2)) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"THREAD CRASH BIG_INT ERROR: x2")
endif
else
call TriggerEvaluate(x2)
endif
endloop
set y=0
set a=td
set w=a
set q=a
set k=b[a].size
set m=a
loop
if (e[n[a]]) then
if (0==n[0]) then
set i=i+1
set m=i
else
set m=n[0]
set n[0]=n[m]
endif
set p[m]=a
set n[m]=w
set n[a]=m
set p[w]=m
set v[m]=0
endif
set a=n[a]
set y=y+v[a]
set v[a]=y-y/k*k
set y=y/k
exitwhen e[n[a]] and 0==y
endloop
return true
endmethod
method multiplyBig takes BigInt k returns nothing
debug if (b[k]==b[this]) then
if (0<v[p[k]]) then
set td=this
set my=k
//call m1
static if DEBUG_MODE then
if (not TriggerEvaluate(x1)) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"THREAD CRASH BIG_INT ERROR: x1")
endif
else
call TriggerEvaluate(x1)
endif
elseif (0==v[p[k]] and not e[p[this]]) then
set n[p[this]]=0
set n[0]=n[this]
set n[this]=this
set p[this]=this
endif
debug elseif (b[k]!=b[this]) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"BIG_INT ERROR: "+I2S(this)+" base not equal to "+I2S(k))
debug endif
endmethod
static method create takes Base k returns thistype
local integer y
local integer z
//allocate a head
if (0==n[0]) then
set i=i+1
set z=i
else
set z=n[0]
set n[0]=n[z]
endif
set e[z]=true
set b[z]=k
set n[z]=z
set p[z]=z
set v[z]=0
return z
endmethod
//add
private static method a0 takes nothing returns boolean
local integer y=td
local integer a
local integer z=my3
local integer k=td2
//first go to the correct digit
//z refers to the *extra* g, so just loop from right to left
//until no remaining g
//1 g of 1 in base 10 would actually be 1*10^1, or 10
loop
if (e[n[y]]) then
if (0==n[0]) then
set i=i+1
set a=i
else
set a=n[0]
set n[0]=n[a]
endif
set p[a]=y
set n[a]=td
set n[y]=a
set p[td]=a
set v[a]=0
endif
set y=n[y]
exitwhen 0==z
set z=z-1
endloop
set z=b[td].size
//this will add the digit to the array
//example in base 10 of 10+13
//{1,0}+13p0 (1,0+13) (d being digit to add to)
//{1,13}+0d1 (13/10=1, 13%10=3)
//{1,3}+1d1 (1+1,3)
//{2,3}+0d2 (exit as 2 < 10) -> (exitwhen v[y] < z)
loop
set v[y]=v[y]+k
exitwhen z>v[y]
set k=v[y]/z
set v[y]=v[y]-k*z
if (e[n[y]]) then
if (0==n[0]) then
set i=i+1
set a=i
else
set a=n[0]
set n[0]=n[a]
endif
set p[a]=y
set n[a]=td
set n[y]=a
set p[td]=a
set v[a]=0
endif
set y=n[y]
endloop
return true
endmethod
//adds k to number
//k may be any positive whole number
method add takes integer k,integer g returns nothing
debug if 0<=g then
if (0<k) then
set td=this
set my3=g
set td2=k
static if DEBUG_MODE then
//call a0 (directly above)
if (not TriggerEvaluate(y0)) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"THREAD CRASH BIG_INT ERROR: y0")
endif
else
call TriggerEvaluate(y0)
endif
endif
debug endif
endmethod
//multiply
private static method ml takes nothing returns boolean
local integer y=td
local integer z=b[td].size
local integer a=0
local integer k=my
local integer q
//first multiply every single digit by the multiplier
loop
set y=n[y]
exitwhen e[y]
set v[y]=v[y]*k
endloop
set y=td
//loop through the number and re add any digits that are >= the base
//efficient adding as the g is already known (g is current node +1)
loop
set y=n[y]
exitwhen e[y]
set q=y
loop
exitwhen z>v[q]
if (e[n[q]]) then
if (0==n[0]) then
set i=i+1
set a=i
else
set a=n[0]
set n[0]=n[a]
endif
set p[a]=q
set n[a]=td
set n[q]=a
set p[td]=a
set v[a]=0
endif
set v[n[q]]=v[n[q]]+v[q]/z
set v[q]=v[q]-v[q]/z*z
endloop
endloop
return true
endmethod
method multiply takes integer k returns nothing
if (0<k) then
if (0<v[p[this]]) then
set td=this
set my=k
//call ml
static if DEBUG_MODE then
if (not TriggerEvaluate(x)) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"THREAD CRASH BIG_INT ERROR: x")
endif
else
call TriggerEvaluate(x)
endif
endif
elseif (0==k and not e[p[this]]) then
set n[p[this]]=n[0]
set n[0]=n[this]
set n[this]=this
set p[this]=this
endif
endmethod
//adds number to left
method enq takes integer k returns nothing
local integer y=p[this]
local integer z
local integer a=b[this].size
//add the digit to itself, this way it doesn't mix with
//the current number and it is grouped properly into the base
loop
if (0==n[0]) then
set i=i+1
set z=i
else
set z=n[0]
set n[0]=n[z]
endif
set p[z]=y
set n[z]=n[y]
set n[y]=z
set p[n[z]]=z
set v[z]=k
set y=z
exitwhen a>v[y]
set k=v[y]/a
set v[y]=v[y]-k*a
endloop
endmethod
//removes number from left
method deq takes nothing returns integer
local thistype z=p[this]
if (not e[z]) then
set p[this]=p[z]
set n[p[z]]=this
set n[z]=n[0]
set n[0]=z
endif
return v[z]
endmethod
//pushes number on to number
//push(10,10) = 1010
method push takes integer k returns nothing
local integer y=this
local integer z
local integer a=b[this].size
//add the digit to itself, this way it doesn't mix with
//the current number and it is grouped properly into the base
loop
if (0==n[0]) then
set i=i+1
set z=i
else
set z=n[0]
set n[0]=n[z]
endif
set p[z]=y
set n[z]=n[y]
set n[y]=z
set p[n[z]]=z
set v[z]=k
set y=z
exitwhen a>v[y]
set k=v[y]/a
set v[y]=v[y]-k*a
endloop
endmethod
//simply pops off the right most digit and returns it
method pop takes nothing returns integer
local thistype z=n[this]
if (not e[z]) then
set n[this]=n[z]
set p[n[z]]=this
set n[z]=n[0]
set n[0]=z
endif
return v[z]
endmethod
//rotate right
method rr takes nothing returns nothing
local integer z
set z=p[this]
set n[z]=n[this]
set p[n[z]]=z
set p[this]=p[z]
set n[p[z]]=this
set p[z]=this
set n[this]=z
endmethod
//rotate left
method rl takes nothing returns nothing
local integer z
set z=n[this]
set p[z]=p[this]
set n[p[z]]=z
set n[this]=n[z]
set p[n[z]]=this
set n[z]=this
set p[this]=z
endmethod
//mod
private static method m0 takes nothing returns boolean
local integer z=0
local integer q=td
local integer u=b[q].size
local integer k=my
loop
set q=p[q]
exitwhen e[q]
set z=(z*u+v[q])
set z=z-z/k*k
endloop
set dz=z
return true
endmethod
method mod takes integer k returns integer
set td=this
set my=k
//call m0
static if DEBUG_MODE then
if not TriggerEvaluate(y7) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"THREAD CRASH BIG_INT ERROR: y7")
endif
else
call TriggerEvaluate(y7)
endif
return dz
endmethod
//destroys the entire integer
method destroy takes nothing returns nothing
debug if (e[this]) then
set n[p[this]]=n[0]
set n[0]=this
set e[this]=false
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"BIG_INT ERROR: ATTEMPT TO DESTROY NULL BIG INT "+I2S(this))
debug endif
endmethod
//toString
private static method tos takes nothing returns boolean
local string q=""
local integer y=td
local Base z=b[y]
//loop through the entire number and convert each integer into a
//char
loop
set y=n[y]
exitwhen e[y]
set q=z.char(v[y])+q
endloop
if (q=="") then
set yy=z.char(0)
else
set yy=q
endif
return true
endmethod
method toString takes nothing returns string
set td=this
//call tos
static if DEBUG_MODE then
if not TriggerEvaluate(y6) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"THREAD CRASH BIG_INT ERROR: y6")
endif
else
call TriggerEvaluate(y6)
endif
return yy
endmethod
method operator base= takes Base k returns nothing
if (b[this].size!=k.size and not e[n[this]]) then
set td=this
set td2=k
//call ch
static if DEBUG_MODE then
if not TriggerEvaluate(j) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"THREAD CRASH BIG_INT ERROR: j")
endif
else
call TriggerEvaluate(j)
endif
endif
set b[this]=k
static if DEBUG_MODE then
if (8191<i) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"OVERFLOW BIG_INT ERROR: "+I2S(i))
endif
endif
endmethod
//convert to any base
private static method ch takes nothing returns boolean
local integer q=td
local integer u
local integer k
//first allocate a new big int to perform operations on
if (0==n[0]) then
set i=i+1
set k=i
else
set k=n[0]
set n[0]=n[k]
endif
set e[k]=true
set u=p[q]
set p[q]=p[u]
set p[k]=u
set n[k]=u
set p[u]=k
set n[u]=k
set b[k]=td2
set v[k]=0
set my=Base(b[td]).size
set my3=0
set td=k
set td2=v[u]
set v[u]=0
//loop through the number from left to right
//add current digit to the new big int
//exitwhen out of digits
//multiply new big int by current base of number, not the target base
//no need to allocate new digits as that's done in add/multiply
loop
//call add
static if DEBUG_MODE then
if not TriggerEvaluate(y0) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"THREAD CRASH BIG_INT ERROR: y0")
endif
else
call TriggerEvaluate(y0)
endif
exitwhen e[p[q]]
//call multiply
static if DEBUG_MODE then
if not TriggerEvaluate(x) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"THREAD CRASH BIG_INT ERROR: x")
endif
else
call TriggerEvaluate(x)
endif
//go to next digit and deallocate it
set u=p[q]
set p[q]=p[u]
set td2=v[u]
set n[u]=n[0]
set n[0]=u
endloop
//make number point to digits in the new big int that was worked on
//then deallocate the new big int
set n[q]=n[k]
set p[q]=p[k]
set p[n[k]]=q
set n[p[k]]=q
set n[k]=n[0]
set n[0]=k
set e[k]=false
return true
endmethod
//only use when big int is holding value that can fit into an integer
method toInt takes nothing returns integer
local integer k=0
local integer z=b[this].size
loop
set this=p[this]
exitwhen e[this]
set k=k*z
set k=k+v[this]
endloop
return k
endmethod
//divide
//done via long division, but nested loops split into two methods,
//so can't really be commented on in a way that's understandable
//it goes from left to right, just like in long division, dividing
//each digit by w/e and using the remainders in the new number
//returns the last remainder
//does not perform any subtraction! It's all modulos math ;D
private static method div takes nothing returns boolean
local integer z=0
local integer a=td
local integer y=a
local integer h=b[a].size
local integer k=my
set y=p[y]
loop
set z=z*h+v[y]
exitwhen e[p[y]] or z>=k
set n[y]=n[0]
set n[0]=y
set y=p[y]
endloop
set p[a]=y
set n[y]=a
loop
set v[y]=z/k
set z=z-v[y]*k
set y=p[y]
exitwhen e[y]
set z=z*h+v[y]
endloop
set y=a
loop
set y=p[y]
exitwhen 0!=v[y] or e[y]
set n[y]=n[0]
set n[0]=y
endloop
set p[a]=y
set n[y]=a
set my=z
return true
endmethod
//returns remainder
method divide takes integer k returns integer
if (0<k and 0<v[p[this]]) then
set td=this
set my=k
static if DEBUG_MODE then
if not TriggerEvaluate(x4) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"THREAD CRASH BIG_INT ERROR: x4")
endif
else
call TriggerEvaluate(x4)
endif
else
return 0
endif
return my
endmethod
private static method s2 takes nothing returns boolean
local Base d=td
local string k=yy
local integer y
local integer z
local integer h=StringLength(k)
if (not d.isValid(k)) then
set td=0
return true
endif
if (0==n[0]) then
set i=i+1
set z=i
else
set z=n[0]
set n[0]=n[z]
endif
set e[z]=true
set b[z]=d
set n[z]=z
set p[z]=z
set v[z]=0
loop
set h=h-1
if (0==n[0]) then
set i=i+1
set y=i
else
set y=n[0]
set n[0]=n[y]
endif
set v[y]=d.ord(SubString(k,h,h+1))
set p[y]=p[z]
set n[y]=z
set n[p[z]]=y
set p[z]=y
exitwhen 0==h
endloop
set td=z
return true
endmethod
static method convertString takes string k,Base y returns thistype
set yy=k
set td=y
//call s2
static if DEBUG_MODE then
if not TriggerEvaluate(x5) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"THREAD CRASH BIG_INT ERROR: x5")
endif
else
call TriggerEvaluate(x5)
endif
return td
endmethod
method operator base takes nothing returns Base
return b[this]
endmethod
implement IntMod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Table // made by Bribe, special thanks to Nestharus, version 3.0.0.0
/*
API
------------
struct Table
| static method create takes nothing returns Table
| create a new Table
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush all stored values inside of it
|
| method remove takes integer key returns nothing
| remove the value at index "key"
|
| method operator []= takes integer key, $TYPE$ value returns nothing
| assign "value" to index "key"
|
| method operator [] takes integer key returns $TYPE$
| load the value at index "key"
|
| method has takes integer key returns boolean
| whether or not the key was assigned
|
----------------
struct TableArray
| static method operator [] takes integer array_size returns TableArray
| create a new array of Tables of size "array_size"
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush and destroy it
|
| method operator size takes nothing returns integer
| returns the size of the TableArray
|
| method operator [] takes integer key returns Table
| returns a Table accessible exclusively to index "key"
*/
globals
private hashtable ht = InitHashtable() //The last hashtable you need
private integer more = 2 //Index generation for Tables (above 2)
private integer less = 0 //Index generation for TableArrays (below 0)
endglobals
private struct dex extends array
static method operator size takes nothing returns Table
return 1
endmethod
static method operator list takes nothing returns Table
return 2
endmethod
endstruct
private struct handles extends array
method has takes integer key returns boolean
return HaveSavedHandle(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSavedHandle(ht, this, key)
endmethod
endstruct
private struct agents extends array
method operator []= takes integer key, agent value returns nothing
call SaveAgentHandle(ht, this, key, value)
endmethod
endstruct
//! textmacro NEW_ARRAY_BASIC takes SUPER, FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$(ht, this, key, value)
endmethod
method has takes integer key returns boolean
return HaveSaved$SUPER$(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSaved$SUPER$(ht, this, key)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//! textmacro NEW_ARRAY takes FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$Handle(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$Handle(ht, this, key, value)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//! runtextmacro NEW_ARRAY_BASIC("Real", "Real", "real")
//! runtextmacro NEW_ARRAY_BASIC("Boolean", "Boolean", "boolean")
//! runtextmacro NEW_ARRAY_BASIC("String", "Str", "string")
//! runtextmacro NEW_ARRAY("Player", "player")
//! runtextmacro NEW_ARRAY("Widget", "widget")
//! runtextmacro NEW_ARRAY("Destructable", "destructable")
//! runtextmacro NEW_ARRAY("Item", "item")
//! runtextmacro NEW_ARRAY("Unit", "unit")
//! runtextmacro NEW_ARRAY("Ability", "ability")
//! runtextmacro NEW_ARRAY("Timer", "timer")
//! runtextmacro NEW_ARRAY("Trigger", "trigger")
//! runtextmacro NEW_ARRAY("TriggerCondition", "triggercondition")
//! runtextmacro NEW_ARRAY("TriggerAction", "triggeraction")
//! runtextmacro NEW_ARRAY("TriggerEvent", "event")
//! runtextmacro NEW_ARRAY("Force", "force")
//! runtextmacro NEW_ARRAY("Group", "group")
//! runtextmacro NEW_ARRAY("Location", "location")
//! runtextmacro NEW_ARRAY("Rect", "rect")
//! runtextmacro NEW_ARRAY("BooleanExpr", "boolexpr")
//! runtextmacro NEW_ARRAY("Sound", "sound")
//! runtextmacro NEW_ARRAY("Effect", "effect")
//! runtextmacro NEW_ARRAY("UnitPool", "unitpool")
//! runtextmacro NEW_ARRAY("ItemPool", "itempool")
//! runtextmacro NEW_ARRAY("Quest", "quest")
//! runtextmacro NEW_ARRAY("QuestItem", "questitem")
//! runtextmacro NEW_ARRAY("DefeatCondition", "defeatcondition")
//! runtextmacro NEW_ARRAY("TimerDialog", "timerdialog")
//! runtextmacro NEW_ARRAY("Leaderboard", "leaderboard")
//! runtextmacro NEW_ARRAY("Multiboard", "multiboard")
//! runtextmacro NEW_ARRAY("MultiboardItem", "multiboarditem")
//! runtextmacro NEW_ARRAY("Trackable", "trackable")
//! runtextmacro NEW_ARRAY("Dialog", "dialog")
//! runtextmacro NEW_ARRAY("Button", "button")
//! runtextmacro NEW_ARRAY("TextTag", "texttag")
//! runtextmacro NEW_ARRAY("Lightning", "lightning")
//! runtextmacro NEW_ARRAY("Image", "image")
//! runtextmacro NEW_ARRAY("Ubersplat", "ubersplat")
//! runtextmacro NEW_ARRAY("Region", "region")
//! runtextmacro NEW_ARRAY("FogState", "fogstate")
//! runtextmacro NEW_ARRAY("FogModifier", "fogmodifier")
//! runtextmacro NEW_ARRAY("Hashtable", "hashtable")
struct Table extends array
// Implement modules for intuitive type-syntax
implement realm
implement booleanm
implement stringm
implement playerm
implement widgetm
implement destructablem
implement itemm
implement unitm
implement abilitym
implement timerm
implement triggerm
implement triggerconditionm
implement triggeractionm
implement eventm
implement forcem
implement groupm
implement locationm
implement rectm
implement boolexprm
implement soundm
implement effectm
implement unitpoolm
implement itempoolm
implement questm
implement questitemm
implement defeatconditionm
implement timerdialogm
implement leaderboardm
implement multiboardm
implement multiboarditemm
implement trackablem
implement dialogm
implement buttonm
implement texttagm
implement lightningm
implement imagem
implement ubersplatm
implement regionm
implement fogstatem
implement fogmodifierm
implement hashtablem
method operator handle takes nothing returns handles
return this
endmethod
method operator agent takes nothing returns agents
return this
endmethod
// set this = a[GetSpellAbilityId()]
method operator [] takes integer key returns Table
return LoadInteger(ht, this, key)
endmethod
// set a[389034] = 8192
method operator []= takes integer key, Table a returns nothing
call SaveInteger(ht, this, key, a)
endmethod
// set b = a.has(2493223)
method has takes integer key returns boolean
return HaveSavedInteger(ht, this, key)
endmethod
// call a.remove(294080)
method remove takes integer key returns nothing
call RemoveSavedInteger(ht, this, key)
endmethod
// Remove all data from a Table instance
method flush takes nothing returns nothing
call FlushChildHashtable(ht, this)
endmethod
// local Table a = Table.create()
static method create takes nothing returns Table
local Table this = dex.list[0]
if this == 0 then
set more = more + 1
set this = more
else
set dex.list[0] = dex.list[this]
call dex.list.remove(this)
endif
debug set dex.list[this] = -1
return this
endmethod
// Removes all data from a Table instance and recycles its index.
//
// call a.destroy()
//
method destroy takes nothing returns nothing
debug if dex.list[this] != -1 then
debug call BJDebugMsg("Table Error: Tried to double-free instance: " + I2S(this))
debug return
debug endif
call this.flush()
set dex.list[this] = dex.list[0]
set dex.list[0] = this
endmethod
endstruct
struct TableArray extends array
//Returns a new TableArray to do your bidding. Simply use:
//
// local TableArray ta = TableArray[array_size]
//
static method operator [] takes integer array_size returns TableArray
local Table a = dex.size[array_size] //Get the unique recycle list for this array size
local TableArray this = a[0] //The last-destroyed TableArray that had this array size
debug if array_size <= 0 then
debug call BJDebugMsg("TypeError: Invalid specified TableArray size: " + I2S(array_size))
debug return 0
debug endif
if this == 0 then
set less = less - array_size
set this = less
else
set a[0] = a[this] //Set the last destroyed to the last-last destroyed
call a.remove(this) //Clear hash memory
endif
set dex.size[this] = array_size //This remembers the array size
return this
endmethod
//Returns the size of the TableArray
method operator size takes nothing returns integer
return dex.size[this]
endmethod
//da[integer a].unit[integer b] = unit u
//da[integer a][integer c] = integer d
//
//Inline-friendly when not running in debug mode
//
method operator [] takes integer key returns Table
static if DEBUG_MODE then
local integer i = this.size
if i == 0 then
call BJDebugMsg("IndexError: Tried to get key from invalid TableArray instance: " + I2S(this))
return 0
elseif key < 0 or key >= i then
call BJDebugMsg("IndexError: Tried to get key [" + I2S(key) + "] from outside TableArray bounds: " + I2S(i))
return 0
endif
endif
return this + key
endmethod
//Destroys a TableArray without flushing it; assumed you'd call .flush()
//if you want it flushed too. This is public so that if you are flushing
//instances the whole time you don't waste efficiency when disposing the
//TableArray.
//
method destroy takes nothing returns nothing
local Table a = dex.size[this.size]
debug if this.size <= 0 then
debug call BJDebugMsg("TypeError: Tried to destroy an invalid TableArray: " + I2S(this))
debug return
debug endif
if a == 0 then
//Create an array to index recycled instances with their array size
set a = Table.create()
set dex.size[this.size] = a
endif
call dex.size.remove(this) //Clear the array size from hash memory
set a[this] = a[0]
set a[0] = this
endmethod
//All you need to know about this one is that it won't hit the op limit.
private static method clean takes Table a, integer end returns nothing
local integer i = a + 5000
if i < end then
call clean.evaluate(i, end)
set end = i
endif
loop
call a.flush()
set a = a + 1
exitwhen a == end
endloop
endmethod
//Flushes the TableArray and also destroys it. Doesn't get any more
//similar to the FlushParentHashtable native than this.
//
method flush takes nothing returns nothing
local integer end = this.size + this
debug if this == end then
debug call BJDebugMsg("TypeError: Tried to flush an invalid TableArray instance: " + I2S(this))
debug return
debug endif
call clean.evaluate(this, end)
call this.destroy()
endmethod
endstruct
endlibrary
//TESH.scrollpos=5
//TESH.alwaysfold=0
library Catalogs uses Catalog
/*************************************************************************************
*
* The syntax for catalogs is Name + Catalog + Encoder Version
*
* The syntax for the macros is CATALOG_ + ENCODER VERSION
*
* For more information on catalogs
*
* hiveworkshop.com/forums/jass-ai-scripts-tutorials-280/saving-loading-192851/#2
*
*************************************************************************************/
//General catalog for Encoder Version 1
//Catalog + 1
struct Catalog1 extends array
implement Catalog
private static method onInit takes nothing returns nothing
endmethod
endstruct
/*
//HeroCatalog for Encoder Version 2
//Hero + Catalog + 2
struct HeroCatalog2 extends array
implement Catalog
private static method onInit takes nothing returns nothing
endmethod
endstruct
*/
endlibrary
//TESH.scrollpos=48
//TESH.alwaysfold=0
//! textmacro ENCODER_SETTINGS
globals
/*************************************************************************************
*
* Save/Load character sets
*
* A characeter set determines what characetrs can pop up in a code. The more
* characters, the smaller the code. However, some characters are hard to tell apart.
*
* These characters are a very safe bet as they are very easy to read
* abcdefghijklmnopqrstuvwxyzABCDEFGHKLMNOPQRSTUVWXYZ0123456789@#$%&?
*
*************************************************************************************/
/*************************************************************************************
*
* ONLINE vs OFFLINE?
*
* BASE refers to the character set for online games.
*
* BASE_OFFLINE refers to the character set for offline games.
*
* Two different character sets ensure that online codes can't load in offline games
* and offline codes can't load in online games.
*
* This is smarter than encoding a flag determining whether the code was an online or
* offline code into the code as it keeps the code smaller.
*
*************************************************************************************/
private constant string BASE = "abcdefghijlkmnopqrstuvwxyzABCDEFGHKLMNOPQRSTUVWXYZ0123456789@#$%&?"
private constant string BASE_OFFLINE = "abcdefghijlkmnopqrstuvwxyzABCDEFGHKLMNOPQRSTUVWXYZ0123456789@#$%&?"
/*************************************************************************************
*
* Security
*
* Security is the maximum checksum value codes can have. Checksums make it so
* that players can't tamper with the code. For example, a checksum of 111 means
* that only 1 in 111 codes will work.
*
* The larger the checksum, the bigger the code size will be. Most checksums are
* around 6 digits, but codes that save very little data may have larger checksums.
*
* The maximum possible checksum is 214748364.
*
*************************************************************************************/
private constant integer SECURITY = 111
private constant integer SECURITY_OFFLINE = 111
/*************************************************************************************
*
* Encoders
*
* ENCODER_VER refers to the current encoder version. When adding new items to a
* map, this changes the save/load code. Because of this, a map needs to support
* multiple save/load codes. This allows for backwards compatibility. For example,
* if a new version of the map is released, players can load up their codes using
* the previous encoder version.
*
* ENCODER_COUNT is how many encoder versions there are.
*
* When dealing with backwards compatibility, a map shouldn't support every code
* it has ever had. It should only go back a few versions. When only wanting to
* support a certain number of codes, just overwrite the oldest encoder
* and change the version to the one you overwrote.
*
* Old encoders are used via -load encoderVersion,code
*
*************************************************************************************/
private constant integer ENCODER_VER = 1
private constant integer ENCODER_COUNT = 1
endglobals
//! endtextmacro
//TESH.scrollpos=3
//TESH.alwaysfold=0
/*************************************************************************************
*
* The syntax for Encoders is Encoder+Version
*
* Do not forget to change the encoder version in BuildEncoder
*
*************************************************************************************/
//! textmacro ENCODERS
//Encoder+1
private function Encoder1 takes nothing returns nothing
/********************************************************************************************
*
* Example
*
********************************************************************************************/
local Encoder encoder = BuildEncoder(6,15,1) //min code length 6
//max code length 15
//encoder version 1
local CodeRange range = CodeRange.create(0, 60000)
local CodeRange range2 = CodeRange.create(50,100)
local CodeRange range3 = CodeRange.create(0,100000000)
//link values 0 through 30000 of range to range2
//give link id 1
call range.link(0,30000,range2,1)
call encoder.add(range)
call encoder.add(range3)
endfunction
//Encoder+2
/***************************************************************************
private function Encoder2 takes nothing returns nothing
local Encoder encoder = BuildEncoder(12,24,2) //min code length 12
//max code length 24
//encoder version 2
local CodeRange range = CodeRange.create(0, 1)
call encoder.add(range)
endfunction
***************************************************************************/
//! endtextmacro
//TESH.scrollpos=111
//TESH.alwaysfold=0
//! textmacro ON_LOAD_ATTEMPT
/*************************************************************************************
*
* Command expects
* -load ver, code
* -load code
*
* Be sure to let the players know how to load up older codes.
*
*************************************************************************************/
/*************************************************************************************
*
* ON LOAD SETTINGS
*
*************************************************************************************/
globals
/*************************************************************************************
*
* DELIMITER
*
* Determines the delimiter character for splitting encoder versions from the
* save/load code.
*
* With a comma, the loader expects
*
* -load encoderVersion, code
*
* With a period, the load would expect
*
* -load encoderVersion. code
*
* When a player saves, the encoder version they saved with as well as their code
* is displayed. This means that when a map is updated and it has a new encoder in
* it, the player can load up using the old one.
*
*************************************************************************************/
private constant string DELIMITER = ","
/*************************************************************************************
*
* INVALID_ENCODER
*
* This message is displayed to a player attempting to load a code when the loader
* they attempted to use was invalid
*
*************************************************************************************/
private constant string INVALID_ENCODER="|cffff0000Invalid Encoder"
endglobals
/*************************************************************************************
*
* USER DEFINED SETTINGS
*
*************************************************************************************/
globals
/*************************************************************************************
*
* LOAD_ATTEMPT
*
* The message appended to INVALID_CODE when MAX_LOAD_ATTEMPT is greater than 0.
*
*************************************************************************************/
private constant string LOAD_ATTEMPT=" Load attempt(s) Left"
/*************************************************************************************
*
* MAX_LOAD_ATTEMPT
*
* This is the maximum amount of attempts a player can load an invalid code.
*
* Less than 0 means infinite load attempts.
*
*************************************************************************************/
private constant integer MAX_LOAD_ATTEMPT=5
/*************************************************************************************
*
* LOAD_ONCE
*
* Makes it so that a player can only successfully load a code one time. Once
* the code is loaded for the player, they can no longer load codes.
*
*************************************************************************************/
private constant boolean LOAD_ONCE=true
/*************************************************************************************
*
* INVALID_CODE
*
* This message is displayed to a player attempting to load a code when that
* code is invalid.
*
*************************************************************************************/
private constant string INVALID_CODE = "|cffff0000Invalid Code"
/*************************************************************************************
*
* VALID_CODE
*
* This message is displayed when a player successfully loads a code.
*
*************************************************************************************/
private constant string VALID_CODE="|cff008B00Decoding Successful"
private boolean array loaded //has the player loaded?
private integer array loadsLeft //player loads remaining
endglobals
/*************************************************************************************
*
* EVENTS
*
*************************************************************************************/
//this is called when the player attempts to load a code
//it is run before the code is even checked
private function CanLoad takes integer playerId returns boolean
//if the player hasn't loaded or has load attempst left, return true
return not loaded[playerId] and 0<loadsLeft[playerId]
endfunction
//this is run when the code to be loaded was invalid
private function OnLoadFailure takes integer playerId returns nothing
if (MAX_LOAD_ATTEMPT>0) then //if multiple load attempts, display remaining
set loadsLeft[playerId]=loadsLeft[playerId]-1
call DisplayTimedTextToPlayer(GetTriggerPlayer(),0,0,60,INVALID_CODE+", "+I2S(loadsLeft[playerId])+LOAD_ATTEMPT)
else //if one or infinite load attempts, don't display remaining
call DisplayTimedTextToPlayer(GetTriggerPlayer(),0,0,60,INVALID_CODE)
endif
endfunction
//this is called when the code was successfully loaded
private function OnLoadSuccess takes integer playerId returns nothing
set loaded[playerId]=LOAD_ONCE //set loaded to LOAD_ONCE
//if LOAD_ONCE is false, loaded will be false
//if LOAD_ONCE is true, loaded will be true
call DisplayTimedTextToPlayer(GetTriggerPlayer(),0,0,60,VALID_CODE)
endfunction
//this is called on init and can be used to initialize things like loads remaining
private function OnLoadInit takes nothing returns nothing
local integer maxLoad=MAX_LOAD_ATTEMPT //retrieve max load attempt
local integer playerId=11
if (0>maxLoad) then //if max load attempt is less than 0, it is infinite loads
//load attempts only reduced if load attempt is greater than 0
//so setting this to 2 will make it stay at 2 forever
set maxLoad=2
endif
//loop through all players and set their remaining loads
loop
set loadsLeft[playerId]=maxLoad
exitwhen 0==playerId
set playerId=playerId-1
endloop
endfunction
//! endtextmacro
//TESH.scrollpos=3
//TESH.alwaysfold=0
//this is run when a player saves
//the player, the player's id, and the buffer are retrievable
//! textmacro ON_SAVE
/********************************************************************************************
*
* FIELDS
*
* buffer: data buffer
* triggerPlayer: saving player
* playerId: id of saving player
*
********************************************************************************************/
/********************************************************************************************
*
* Example
*
********************************************************************************************/
local integer save1=GetRandomInt(0,60000)
local integer save2=GetRandomInt(50,100)
local integer save3=GetRandomInt(0,100000000)
//value can go from 0 to 60,000
call buffer.write(save1) //can fit values between 0 and 1
//display saved value
call DisplayTimedTextToPlayer(GetTriggerPlayer(),0,0,60,"Saved: " + I2S(save1))
//if value between 500 and 50,000, buffer.id will be 1
if (buffer.id==1) then
call buffer.write(save2)
call DisplayTimedTextToPlayer(GetTriggerPlayer(),0,0,60,"Saved: " + I2S(save2))
endif
call buffer.write(save3)
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"Saved: " + I2S(save3))
//! endtextmacro
//this is run when the code is ready to be displayed
//the player, the player's id, and the buffer are retrievable
//! textmacro ON_DISPLAY_CODE
/********************************************************************************************
*
* FIELDS
*
* buffer: data buffer
* triggerPlayer: saving player
* playerId: id of saving player
*
********************************************************************************************/
//Encoder Version
call DisplayTimedTextToPlayer(triggerPlayer, 0, 0, 60, "Encoder Version: " + GetCurrentEncoder().toString())
//Save/Load code
call DisplayTimedTextToPlayer(triggerPlayer, 0, 0, 60, buffer.code)
//! endtextmacro
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*************************************************************************************
*
* Loaders are run when a player loads.
*
* The syntax of a loader is Load + Encoder Version.
*
*************************************************************************************/
//! textmacro LOADERS
//Load + 1
private function Load1 takes nothing returns nothing
local player triggerPlayer = GetTriggerPlayer() //the loading player
local integer playerId = GetPlayerId(triggerPlayer) //the loading player id
local DataBuffer buffer = bf //the buffer containing the data
/********************************************************************************************
*
* Example
*
********************************************************************************************/
call DisplayTimedTextToPlayer(triggerPlayer,0,0,60,"Loaded: " + I2S(buffer.read()))
if (1==buffer.id) then
call DisplayTimedTextToPlayer(triggerPlayer,0,0,60, "Loaded : " + I2S(buffer.read()))
endif
call DisplayTimedTextToPlayer(triggerPlayer,0,0,60, "LOaded: " + I2S(buffer.read()))
set triggerPlayer=null
endfunction
//Load + 2
/***************************************************************************
private function Load2 takes nothing returns nothing
local player triggerPlayer = GetTriggerPlayer() //the loading player
local integer playerId = GetPlayerId(triggerPlayer) //the loading player id
local DataBuffer buffer = bf //the buffer containing the data
endfunction
***************************************************************************/
//! endtextmacro
//TESH.scrollpos=1
//TESH.alwaysfold=0
library SaveLoad uses Catalogs, Encoders
globals
private DataBuffer bf
endglobals
//! runtextmacro LOADERS()
//! runtextmacro ON_LOAD_ATTEMPT()
private function Save takes nothing returns boolean
local player triggerPlayer = GetTriggerPlayer() //the saving player
local integer playerId = GetPlayerId(triggerPlayer) //the id of the saving player
local DataBuffer buffer = GetCurrentEncoder().write(playerId) //a data buffer to write to
//! runtextmacro ON_SAVE()
//! runtextmacro ON_DISPLAY_CODE()
set triggerPlayer=null
return false
endfunction
private function Loader takes nothing returns boolean
local string s
local string c
local integer i
local integer q
local string s2
local string s3
local integer pid=GetPlayerId(GetTriggerPlayer())
local Encoder enc
//if the player hasn't loaded and player still has load attempts left
if (CanLoad(pid)) then
//parse code
set s=GetEventPlayerChatString()
set s=SubString(s,6,StringLength(s))
set q = StringLength(s)
set bf=0
set s2=""
set s3=""
set i=0
loop
exitwhen i==q
set c=SubString(s,i,i+1)
exitwhen c==DELIMITER
if (" "!=c) then
set s2=s2+c
endif
set i=i+1
endloop
//if parsed code used an older encoder, read it using that encoder
if (i<q) then
set s=SubString(s,i+1,q)
set i=q-i-1
loop
exitwhen 0==i
set c=SubString(s,i-1,i)
if (" "!=c) then
set s3=c+s3
endif
set i=i-1
endloop
set enc=Encoder.convertString(s2)
if (0!=enc) then
set bf=enc.read(s3, pid)
else
call DisplayTimedTextToPlayer(GetTriggerPlayer(),0,0,60,INVALID_ENCODER)
return false
endif
//read using latest encoder
else
set enc=GetCurrentEncoder()
set bf=enc.read(s,pid)
endif
if (0!=bf) then
call OnLoadSuccess(pid)
call ExecuteFunc(SCOPE_PRIVATE+"Load"+I2S(GetEncoderVer(enc)))
else
call OnLoadFailure(pid)
endif
endif
return false
endfunction
private struct SaveLoad extends array
//register save/load commands
//set up max load attempt
private static method onInit takes nothing returns nothing
local trigger t=CreateTrigger()
local trigger t2=CreateTrigger()
local integer i=11
local player p
loop
set p=Player(i)
if (GetPlayerSlotState(p)==PLAYER_SLOT_STATE_PLAYING and GetPlayerController(p)==MAP_CONTROL_USER) then
call TriggerRegisterPlayerChatEvent(t,p,"-save",true)
call TriggerRegisterPlayerChatEvent(t2,p,"-load",false)
endif
exitwhen 0==i
set i=i-1
endloop
call TriggerAddCondition(t,Condition(function Save))
call TriggerAddCondition(t2,Condition(function Loader))
set t=null
set t2=null
set p=null
call OnLoadInit()
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Encoders uses Catalogs, Encoder
//! runtextmacro ENCODER_SETTINGS()
globals
private integer curEncoder = 0
private boolean online = false
private integer array encoders
private integer array encoderVer
endglobals
function GetCurrentEncoder takes nothing returns Encoder
return curEncoder
endfunction
function GetEncoder takes integer ver returns integer
return encoders[ver]
endfunction
function GetEncoderVer takes integer id returns integer
return encoderVer[id]
endfunction
private function BuildEncoder takes integer minCodeLength,integer maxCodeLength,integer ver returns Encoder
local Encoder encoder
//encoder is created based on online flag
if (online) then
set encoder=Encoder.create(BASE,minCodeLength,maxCodeLength,SECURITY+ver,ver)
else
set encoder=Encoder.create(BASE_OFFLINE,minCodeLength,maxCodeLength,SECURITY_OFFLINE+ver, ver)
endif
set encoders[ver]=encoder
set encoderVer[encoder]=ver
if (ver==ENCODER_VER) then
set curEncoder=encoder
endif
return encoder
endfunction
//! runtextmacro ENCODERS()
private struct EncodersInit extends array
private static method onInit takes nothing returns nothing
//build Encoders and set online flag
local integer i=ENCODER_COUNT
set online = not ReloadGameCachesFromDisk()
if (0<ENCODER_COUNT) then
loop
exitwhen 0==i
call ExecuteFunc(SCOPE_PRIVATE+"Encoder"+I2S(i))
set i=i-1
endloop
endif
endmethod
endstruct
endlibrary