//TESH.scrollpos=0
//TESH.alwaysfold=0
Name | Type | is_array | initial_value |
//TESH.scrollpos=0
//TESH.alwaysfold=0
// Written by BPower.
scope InventoryGeneneratObjects/*
*******************************************************************************
*
* This small snippet will help you to quickly generate objects
* for the Inventory user interface via LUA script.
* Change the code below to your needs.
*
* To start the object generation process set INVENTORY_GENERATE_OBJECTS in the user settings to "true".
* The save and close your map. Once you open it again the objects are there.
* Then change the true back to "false"
*
* Make sure you have the UIBaseButton.mdx in your map.
*
*******************************************************************************
*/
//! runtextmacro INVENTORY_GEN_START("INVENTORY_GENERATE_OBJECTS")
//! runtextmacro INVENTORY_GEN_BUTTON("Biba", "#INV (Cancel)", "ReplaceableTextures\\CommandButtons\\BTNCancel.blp")
//! runtextmacro INVENTORY_GEN_BUTTON("Bibb", "#INV (Drop Item)", "ReplaceableTextures\\CommandButtons\\BTNPickUpItem.blp")
//! runtextmacro INVENTORY_GEN_BUTTON("Bibc", "#INV (Pawn Item)", "ReplaceableTextures\\CommandButtons\\BTNChestOfGold.blp")
//! runtextmacro INVENTORY_GEN_BUTTON("Bibd", "#INV (Pawn Item)", "ReplaceableTextures\\CommandButtons\\BTNLoad.blp")
//! runtextmacro INVENTORY_GEN_BUTTON("Bica", "#INV [Amulet Class]", "ReplaceableTextures\\CommandButtonsDisabled\\DISBTNAmulet.blp")
//! runtextmacro INVENTORY_GEN_BUTTON("Bicb", "#INV [Boot Class]", "ReplaceableTextures\\CommandButtonsDisabled\\DISBTNBoots.blp")
//! runtextmacro INVENTORY_GEN_BUTTON("Bicc", "#INV [Glove Class]", "ReplaceableTextures\\CommandButtonsDisabled\\DISBTNGauntletsOfOgrePower.blp")
//! runtextmacro INVENTORY_GEN_BUTTON("Bicd", "#INV [Weapon Class]", "ReplaceableTextures\\CommandButtonsDisabled\\DISBTNSteelMelee.blp")
//! runtextmacro INVENTORY_GEN_BUTTON("Bice", "#INV [Helmet Class]", "ReplaceableTextures\\CommandButtonsDisabled\\DISBTNHoodOfCunning.blp")
//! runtextmacro INVENTORY_GEN_BUTTON("Bicf", "#INV [Chest Class]", "ReplaceableTextures\\CommandButtonsDisabled\\DISBTNImprovedMoonArmor.blp")
//! runtextmacro INVENTORY_GEN_BUTTON("Bicg", "#INV [Wrist Class]", "ReplaceableTextures\\CommandButtonsDisabled\\DISBTNGlove.blp")
//! runtextmacro INVENTORY_GEN_BUTTON("Bich", "#INV [Ring Class]", "ReplaceableTextures\\CommandButtonsDisabled\\DISBTNGoldRing.blp")
//! runtextmacro INVENTORY_GEN_BUTTON("Bici", "#INV [Shoulder Class]", "ReplaceableTextures\\CommandButtonsDisabled\\DISBTNAdvancedMoonArmor.blp")
//! runtextmacro INVENTORY_GEN_BUTTON("Bicj", "#INV [Pant Class]", "ReplaceableTextures\\CommandButtonsDisabled\\DISBTNSlippersOfAgility.blp")
//! runtextmacro INVENTORY_GEN_BUTTON("Bick", "#INV [Belt Class]", "ReplaceableTextures\\CommandButtonsDisabled\\DISBTNBelt.blp")
//! runtextmacro INVENTORY_GEN_SPECIAL("Bisa", "#INV (Selector)", "war3mapImported\\selector.MDX")
//! runtextmacro INVENTORY_GEN_END()
// these textmacros below generate an LUA file through vJass to send to ObjectMerger
// don't change these unless you know what you are doing.
//! textmacro INVENTORY_GEN_START takes DO_GEN
//! externalblock extension=lua ObjectMerger $FILENAME$
//! i if "$DO_GEN$" == "true" then
//! i setobjecttype("destructables")
//! endtextmacro
//! textmacro INVENTORY_GEN_END
//! i end
//! endexternalblock
//! endtextmacro
//! textmacro INVENTORY_GEN_SPECIAL takes RAWCODE, NAME, MODEL
//! i createobject("YTlb", "$RAWCODE$")
//! i makechange(current, "bfxr", 180.00)
//! i makechange(current, "bfil", "$MODEL$")
//! i makechange(current, "boch", 0.00)
//! i makechange(current, "bshd", "")
//! i makechange(current, "btar", "")
//! i makechange(current, "bcpr", 0)
//! i makechange(current, "bmas", 10.0)
//! i makechange(current, "bmis", 0.10)
//! i makechange(current, "bcpd", 0)
//! i makechange(current, "buch", 0)
//! i makechange(current, "bptx", none)
//! i makechange(current, "bsuf", "$NAME$")
//! i makechange(current, "bnam", " ")
//! endtextmacro
//! textmacro INVENTORY_GEN_BUTTON takes RAWCODE, NAME, TEXTURE
//! i createobject("YTlb", "$RAWCODE$")
//! i makechange(current, "bfxr", 270.00)
//! i makechange(current, "bfil", "war3mapImported\\UIBaseButton.mdl")
//! i makechange(current, "boch", 0.00)
//! i makechange(current, "btxf", "$TEXTURE$")
//! i makechange(current, "btxi", 31)
//! i makechange(current, "bshd", "")
//! i makechange(current, "btar", "")
//! i makechange(current, "bcpr", 0)
//! i makechange(current, "bmas", 10.0)
//! i makechange(current, "bmis", 0.10)
//! i makechange(current, "bcpd", 0)
//! i makechange(current, "buch", 0)
//! i makechange(current, "bptx", none)
//! i makechange(current, "bsuf", "$NAME$")
//! i makechange(current, "bnam", " ")
//! endtextmacro
//! textmacro INVENTORY_GEN_BORDER takes RAWCODE, NAME, MODEL
//! i createobject("YTlb", "$RAWCODE$")
//! i makechange(current, "bfxr", 0.00)
//! i makechange(current, "bfil", "$MODEL$")
//! i makechange(current, "boch", 0.00)
//! i makechange(current, "btxf", "UI\\Widgets\\EscMenu\\Human\\human-options-menu-border.blp")
//! i makechange(current, "btxi", 31)
//! i makechange(current, "bshd", "")
//! i makechange(current, "btar", "")
//! i makechange(current, "bcpr", 0)
//! i makechange(current, "bmas", 10.0)
//! i makechange(current, "bmis", 0.10)
//! i makechange(current, "bcpd", 0)
//! i makechange(current, "buch", 0)
//! i makechange(current, "bptx", none)
//! i makechange(current, "bsuf", "$NAME$")
//! i makechange(current, "bnam", " ")
//! endtextmacro
endscope
//TESH.scrollpos=-1
//TESH.alwaysfold=0
//**
//* Credits:
//* - Nestharus ( Ascii, ErrorMessage )
//* - Bribe ( Ascii, Table )
//* - PitzerMike ( original TextSplat system )
//* - Deaod ( TextSplat system of the second generation, Font )
//**
//* Written by BPower
library Font/*v1.0
*************************************************************************************
*
* Creates custom fonts in Warcraft III.
* Fonts are used to translate strings into a sequence of images.
*
* A custom fonts may contain every character of the Ascii chart,
* as well as file paths to textures inside your map.
*
* Has full backwards compatibility to Deaods Font library.
*
*************************************************************************************
*
* Creating custom fonts:
* ======================
* Refer to wc3c.net/showthread.php?t=87798
* You can change the file paths to your needs, but don't forget to copy
* the width of each individual chars from the .j file that the program creates.
*
*************************************************************************************
*
* */ requires /*
*
* */ Ascii /* hiveworkshop.com/forums/jass-resources-412/snippet-ascii-190746/
* */ Table /* hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
* */ optional ErrorMessage /* github.com/nestharus/JASS/tree/master/jass/Systems/ErrorMessage
*
************************************************************************************
*
* Import instruction:
* ===================
* Copy & paste library Font into your map.
* Make sure your map also has library Table and library Ascii.
* Otherwise copy those two required libraries aswell.
*
* API:
* ====
* struct Font extends array
*
* static method create takes Font parentFont returns Font
* - A parent font is accessed, if a char or file is not available in this font.
*
* method addChar takes string char, real widthInPixel, string filePath returns nothing
* - adds a new char to the font. The char must be part of the Ascii chart.
*
* method getChar takes string char returns FontChar
* - FontChar fields are "width" and "path".
*
* method addImage takes string tag, real widthInPixel, string filePath returns nothing
* - adds an image to the font. Images are fully compatible with parents.
* - to access an image you have to use myFont.getImage(tag)
* - tags are case-insensitive ( myTag is equal to MYtAg )
*
* method getImage takes string tag returns FontImage
* - pass in the tag defined in addImage() to access a FontImage
* - FontImage fields are "width" and "path"
*/
globals
//* Struct font has an instance limit of 31.
//* Only if you need more than 31 fonts in your map set MORE to true.
private constant boolean MORE = false
endglobals
//**
//* Naming convention:
//* ==================
//* Due to backwards compatibility to Deaods Font library
//* I didn't use medial capitalization.
//* Personally I prefer struct names using PascalCase.
private struct fontchar extends array
private static integer alloc = 0
string path
real width
static method create takes string strPath, real strWidth returns thistype
local thistype this = thistype.alloc + 1
set thistype.alloc = integer(this)
set path = strPath
set width = strWidth
return this
endmethod
endstruct
private struct fontimage extends array
private static integer alloc = 0
string path
real width
static method create takes string strPath, real strWidth returns thistype
local thistype this = thistype.alloc + 1
set thistype.alloc = integer(this)
set path = strPath
set width = strWidth
return this
endmethod
endstruct
static if MORE then
private module InitFontTable
private static method onInit takes nothing returns nothing
set thistype.chars = Table.create()
endmethod
endmodule
endif
struct font extends array
private static integer alloc = 0
static if MORE then
private static Table chars
implement optional InitFontTable
else
private static integer array chars//* (this - 1)*256
endif
private thistype parent
private Table imgs
method getChar takes string char returns fontchar
local integer fc = chars[(this - 1)*256 + Char2Ascii(char)]
if (fc != 0) then
return fc
elseif (0 != parent) then
return parent.getChar(char)
endif
static if LIBRARY_ErrorMessage then
debug call ThrowWarning(true, "Font", "getChar", "char", this, "Char [" + char + "] is not available in this font!")
endif
return 0
endmethod
method addChar takes string char, real width, string path returns nothing
local integer index = (this - 1)*256 + Char2Ascii(char)
local fontchar fc = chars[index]
//*
static if LIBRARY_ErrorMessage then
debug call ThrowError((index - ((this - 1)*256) <= 0), "Font", "addChar", "ascii", this, "Char [" + char + "] is not part of the Ascii chart!")
endif
if (fc == 0) then
set chars[index] = fontchar.create(path, width)
else
set fc.path = path
set fc.width = width
endif
endmethod
method getImage takes string file returns fontimage
local integer fi = imgs[StringHash(file)]
if (0 != fi) then
return fi
elseif (0 != parent) then
return parent.getImage(file)
endif
static if LIBRARY_ErrorMessage then
debug call ThrowWarning(true, "Font", "getImage", "file", this, "File [" + file + "] is not available in this font!")
endif
return 0
endmethod
//* To works with both Tables I didn't use t.exists(index) nor t.has(index).
//* Luckily hashtables return 0 for not existant entries.
method addImage takes string file, real width, string path returns nothing
local integer index = StringHash(file)
local fontimage img
if (imgs[index] == 0) then
set imgs[index] = fontimage.create(path, width)
else
set img = imgs[index]
set img.path = path
set img.width = width
endif
endmethod
static method create takes thistype parentFont returns thistype
local thistype this = thistype.alloc + 1
static if LIBRARY_ErrorMessage and not MORE then
debug call ThrowError((this == 32), "Font", "create", "thistype", this, "Overflow. Go to library Font and set boolean MORE to true!")
endif
set thistype.alloc = integer(this)
set parent = parentFont
set imgs = Table.create()
return this
endmethod
//* Backwards compatibility to Deaods Font.
method operator [] takes string char returns fontchar
return getChar(char)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Ascii /* v1.1.0.0 Nestharus/Bribe
************************************************************************************
*
* 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 integer array h //hash2
private integer array y //hash3
private string array c //char
endglobals
function Char2Ascii takes string p returns integer
local integer z = i[StringHash(p)/0x1F0748+0x40D]
if (c[z] != p) then
if (c[z - 32] != p) then
if (c[h[z]] != p) then
if (c[y[z]] != p) then
if (c[83] != p) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"ASCII ERROR: INVALID CHARACTER: " + p)
return 0
endif
return 83
endif
return y[z]
endif
return h[z]
endif
return z - 32
endif
return z
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 a = a*256 + Char2Ascii(SubString(s,j,j+1))
set j=j+1
endloop
return a
endfunction
private module Init
private static method onInit takes nothing returns nothing
set i[966] = 8
set i[1110] = 9
set i[1621] = 10
set i[1375] = 12
set i[447] = 13
set i[233] = 32
set i[2014] = 33
set i[1348] = 34
set i[1038] = 35
set i[1299] = 36
set i[1018] = 37
set i[1312] = 38
set i[341] = 39
set i[939] = 40
set i[969] = 41
set i[952] = 42
set i[2007] = 43
set i[1415] = 44
set i[2020] = 45
set i[904] = 46
set i[1941] = 47
set i[918] = 48
set i[1593] = 49
set i[719] = 50
set i[617] = 51
set i[703] = 52
set i[573] = 53
set i[707] = 54
set i[1208] = 55
set i[106] = 56
set i[312] = 57
set i[124] = 58
set i[1176] = 59
set i[74] = 60
set i[1206] = 61
set i[86] = 62
set i[340] = 63
set i[35] = 64
set i[257] = 65
set i[213] = 66
set i[271] = 67
set i[219] = 68
set i[1330] = 69
set i[1425] = 70
set i[1311] = 71
set i[238] = 72
set i[1349] = 73
set i[244] = 74
set i[1350] = 75
set i[205] = 76
set i[1392] = 77
set i[1378] = 78
set i[1432] = 79
set i[1455] = 80
set i[1454] = 81
set i[1431] = 82
set i[1409] = 83
set i[1442] = 84
set i[534] = 85
set i[1500] = 86
set i[771] = 87
set i[324] = 88
set i[1021] = 89
set i[73] = 90
set i[1265] = 91
set i[1941] = 92
set i[1671] = 93
set i[1451] = 94
set i[1952] = 95
set i[252] = 96
set i[257] = 97
set i[213] = 98
set i[271] = 99
set i[219] = 100
set i[1330] = 101
set i[1425] = 102
set i[1311] = 103
set i[238] = 104
set i[1349] = 105
set i[244] = 106
set i[1350] = 107
set i[205] = 108
set i[1392] = 109
set i[1378] = 110
set i[1432] = 111
set i[1455] = 112
set i[1454] = 113
set i[1431] = 114
set i[1409] = 115
set i[1442] = 116
set i[534] = 117
set i[1500] = 118
set i[771] = 119
set i[324] = 120
set i[1021] = 121
set i[73] = 122
set i[868] = 123
set i[1254] = 124
set i[588] = 125
set i[93] = 126
set i[316] = 161
set i[779] = 162
set i[725] = 163
set i[287] = 164
set i[212] = 165
set i[7] = 166
set i[29] = 167
set i[1958] = 168
set i[1009] = 169
set i[1580] = 170
set i[1778] = 171
set i[103] = 172
set i[400] = 174
set i[1904] = 175
set i[135] = 176
set i[1283] = 177
set i[469] = 178
set i[363] = 179
set i[550] = 180
set i[1831] = 181
set i[1308] = 182
set i[1234] = 183
set i[1017] = 184
set i[1093] = 185
set i[1577] = 186
set i[606] = 187
set i[1585] = 188
set i[1318] = 189
set i[980] = 190
set i[1699] = 191
set i[1292] = 192
set i[477] = 193
set i[709] = 194
set i[1600] = 195
set i[2092] = 196
set i[50] = 197
set i[546] = 198
set i[408] = 199
set i[853] = 200
set i[205] = 201
set i[411] = 202
set i[1311] = 203
set i[1422] = 204
set i[1808] = 205
set i[457] = 206
set i[1280] = 207
set i[614] = 208
set i[1037] = 209
set i[237] = 210
set i[1409] = 211
set i[1023] = 212
set i[1361] = 213
set i[695] = 214
set i[161] = 215
set i[1645] = 216
set i[1822] = 217
set i[644] = 218
set i[1395] = 219
set i[677] = 220
set i[1677] = 221
set i[881] = 222
set i[861] = 223
set i[1408] = 224
set i[1864] = 225
set i[1467] = 226
set i[1819] = 227
set i[1971] = 228
set i[949] = 229
set i[774] = 230
set i[1828] = 231
set i[865] = 232
set i[699] = 233
set i[786] = 234
set i[1806] = 235
set i[1286] = 236
set i[1128] = 237
set i[1490] = 238
set i[1720] = 239
set i[1817] = 240
set i[729] = 241
set i[1191] = 242
set i[1164] = 243
set i[413] = 244
set i[349] = 245
set i[1409] = 246
set i[660] = 247
set i[2016] = 248
set i[1087] = 249
set i[1497] = 250
set i[753] = 251
set i[1579] = 252
set i[1456] = 253
set i[606] = 254
set i[1625] = 255
set h[92] = 47
set h[201] = 108
set h[201] = 76
set h[203] = 103
set h[203] = 71
set h[246] = 115
set h[246] = 83
set h[246] = 211
set h[254] = 187
set y[201] = 108
set y[203] = 103
set y[246] = 115
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[91]="["
set c[92]="\\"
set c[93]="]"
set c[94]="^"
set c[95]="_"
set c[96]="`"
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[123]="{"
set c[124]="|"
set c[125]="}"
set c[126]="~"
set c[128] = "€"
set c[130] = "‚"
set c[131] = "ƒ"
set c[132] = "„"
set c[133] = "…"
set c[134] = "†"
set c[135] = "‡"
set c[136] = "ˆ"
set c[137] = "‰"
set c[138] = "Š"
set c[139] = "‹"
set c[140] = "Œ"
set c[142] = "Ž"
set c[145] = "‘"
set c[146] = "’"
set c[147] = "“"
set c[148] = "”"
set c[149] = "•"
set c[150] = "–"
set c[151] = "—"
set c[152] = "˜"
set c[153] = "™"
set c[154] = "š"
set c[155] = "›"
set c[156] = "œ"
set c[158] = "ž"
set c[159] = "Ÿ"
set c[160] = " "
set c[161] = "¡"
set c[162] = "¢"
set c[163] = "£"
set c[164] = "¤"
set c[165] = "¥"
set c[166] = "¦"
set c[167] = "§"
set c[168] = "¨"
set c[169] = "©"
set c[170] = "ª"
set c[171] = "«"
set c[172] = "¬"
set c[174] = "®"
set c[175] = "¯"
set c[176] = "°"
set c[177] = "±"
set c[178] = "²"
set c[179] = "³"
set c[180] = "´"
set c[181] = "µ"
set c[182] = "¶"
set c[183] = "·"
set c[184] = "¸"
set c[185] = "¹"
set c[186] = "º"
set c[187] = "»"
set c[188] = "¼"
set c[189] = "½"
set c[190] = "¾"
set c[191] = "¿"
set c[192] = "À"
set c[193] = "Á"
set c[194] = "Â"
set c[195] = "Ã"
set c[196] = "Ä"
set c[197] = "Å"
set c[198] = "Æ"
set c[199] = "Ç"
set c[200] = "È"
set c[201] = "É"
set c[202] = "Ê"
set c[203] = "Ë"
set c[204] = "Ì"
set c[205] = "Í"
set c[206] = "Î"
set c[207] = "Ï"
set c[208] = "Ð"
set c[209] = "Ñ"
set c[210] = "Ò"
set c[211] = "Ó"
set c[212] = "Ô"
set c[213] = "Õ"
set c[214] = "Ö"
set c[215] = "×"
set c[216] = "Ø"
set c[217] = "Ù"
set c[218] = "Ú"
set c[219] = "Û"
set c[220] = "Ü"
set c[221] = "Ý"
set c[222] = "Þ"
set c[223] = "ß"
set c[224] = "à"
set c[225] = "á"
set c[226] = "â"
set c[227] = "ã"
set c[228] = "ä"
set c[229] = "å"
set c[230] = "æ"
set c[231] = "ç"
set c[232] = "è"
set c[233] = "é"
set c[234] = "ê"
set c[235] = "ë"
set c[236] = "ì"
set c[237] = "í"
set c[238] = "î"
set c[239] = "ï"
set c[240] = "ð"
set c[241] = "ñ"
set c[242] = "ò"
set c[243] = "ó"
set c[244] = "ô"
set c[245] = "õ"
set c[246] = "ö"
set c[247] = "÷"
set c[248] = "ø"
set c[249] = "ù"
set c[250] = "ú"
set c[251] = "û"
set c[252] = "ü"
set c[253] = "ý"
set c[254] = "þ"
set c[255] = "ÿ"
endmethod
endmodule
private struct Inits extends array
implement Init
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library StringSize /* v2.1.0.0
********************************************************************
*
* This library can calculate the width of a string in pixels.
* Useful for word wrapping in multiboards and texttags.
*
* The sizes might not be 100% accurate but are far more reliable
* than using just StringLength().
* Note that actual sizes may very depending on resolution.
*
*********************************************************************
*
* */uses/*
*
* */ Ascii /* http://www.hiveworkshop.com/forums/jass-functions-413/snippet-ascii-190746/
*
*********************************************************************
*
* function MeasureString takes string source returns real
*
* - Measures the string and returns the calculated width.
*
* function MeasureCharacter takes string char returns real
*
* - Returns the width of an individual character.
*
*********************************************************************
*
* struct StringSize
*
* static method measure takes string source returns real
*
* static method measureChar takes string char returns real
*
*********************************************************************
*
* Credits
* - Bob666 aka N-a-z-g-u-l for the character widths.
* - Tukki for pointing out an error in the system.
*
*********************************************************************/
globals
private real array size
endglobals
private module StringSizeModule
static method onInit takes nothing returns nothing
set size[124] = 3
set size[39] = 4
set size[58] = 4
set size[59] = 4
set size[46] = 4
set size[44] = 4
set size[49] = 5
set size[105] = 5
set size[33] = 5
set size[108] = 6
set size[73] = 6
set size[106] = 6
set size[40] = 6
set size[91] = 6
set size[93] = 6
set size[123] = 6
set size[125] = 6
set size[32] = 7
set size[34] = 7
set size[41] = 7
set size[74] = 7
set size[114] = 8
set size[102] = 8
set size[96] = 8
set size[116] = 9
set size[45] = 9
set size[92] = 9
set size[42] = 9
set size[70] = 10
set size[115] = 11
set size[47] = 11
set size[63] = 11
set size[69] = 12
set size[76] = 12
set size[55] = 12
set size[43] = 12
set size[61] = 12
set size[60] = 12
set size[62] = 12
set size[36] = 12
set size[97] = 12
set size[107] = 13
set size[84] = 13
set size[99] = 13
set size[83] = 13
set size[110] = 13
set size[122] = 13
set size[80] = 13
set size[51] = 13
set size[53] = 13
set size[95] = 13
set size[126] = 13
set size[94] = 13
set size[98] = 14
set size[66] = 14
set size[54] = 14
set size[118] = 14
set size[101] = 14
set size[120] = 14
set size[121] = 14
set size[50] = 14
set size[57] = 14
set size[104] = 14
set size[117] = 14
set size[111] = 15
set size[100] = 15
set size[48] = 15
set size[103] = 15
set size[56] = 15
set size[52] = 15
set size[113] = 15
set size[112] = 15
set size[115] = 15
set size[67] = 16
set size[82] = 16
set size[90] = 16
set size[86] = 16
set size[89] = 16
set size[68] = 16
set size[75] = 16
set size[85] = 16
set size[35] = 16
set size[78] = 17
set size[72] = 17
set size[37] = 17
set size[71] = 18
set size[88] = 18
set size[64] = 18
set size[65] = 19
set size[119] = 20
set size[79] = 20
set size[109] = 21
set size[81] = 21
set size[38] = 21
set size[77] = 25
set size[87] = 26
endmethod
endmodule
struct StringSize extends array
static method measureChar takes string char returns real
return size[Char2Ascii(char)]
endmethod
static method measure takes string s returns real
local integer i = 0
local integer l = StringLength(s)
local real result = 0
local string sub = ""
if l == 0 then
return 0.
elseif l == 1 then
return size[Char2Ascii(s)]
endif
loop
exitwhen i >= l
set sub = SubString(s, i, i+1)
if sub == "|" then
set sub = SubString(s, i+1, i+2)
if sub == "c" then
set i = i + 9
elseif sub == "r" then
set i = i + 1
else
set result = result + size[124]
endif
else
set result = result + size[Char2Ascii(sub)]
endif
set i = i + 1
endloop
return result
endmethod
implement StringSizeModule
endstruct
function MeasureString takes string s returns real
return StringSize.measure(s)
endfunction
function MeasureCharacter takes string s returns real
return StringSize.measureChar(s)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library WordWrap /* v2.1.0.0
*********************************************************************
*
* Simulates word wrapping for multiboards and texttags.
* Word wrapping is where, if a text is too long, it will
* cut to the next line rather than cutting off the text.
*
* How it works:
*
* - Specify a source string and a size margin. The margin
* determines what the size of the line can be before it
* splits the string into fragments. The value is more or
* less arbritrary, so just experiment with values.
* - Explodes the string by spaces into words.
* - Checks the size of each word.
* - Once the size margin is reached, it will cut the string.
* - The first line is stored in the array under the index 0.
* - Rinse and repeat until it fragments the entire string.
* - You can retrieve the count to know the max index reached.
*
*********************************************************************
*
* */uses/*
*
* */ StringSize /* [url]http://wc3jass.com/5014/snippet-stringsize/msg38482/topicseen/#new[/url]
*
* This library does not require Ascii, but StringSize requires
* the Ascii library:
*
* Ascii [url]http://www.hiveworkshop.com/forums/jass-functions-413/snippet-ascii-190746/[/url]
*
*********************************************************************
*
* function WordWrapString takes string source, real sizeMargin, boolean preserveColors returns nothing
*
* - Breaks the string into several fragments.
* - Size margin determines the size the string can reach
* before it fragments the string and breaks to the next
* line.
* - Preserve colors is a boolean to check if a color
* code is being broken during the word wrap. If you
* are not using color codes (ex: |cffffcc00text|r)
* then you should set this to false to save performance.
*
* function GetWrappedStringFragment takes integer index returns string
*
* - Retrieves the fragmented strings. The indices start at 0
* and will go up to the "count" index - 1. (see below) The last
* line will be:
*
* GetWrappedStringFragment(GetWrappedStringCount() - 1)
*
* function GetWrappedStringCount takes nothing returns integer
*
* - Returns the total number of fragments. Indices start at 0,
* and the final line will be this number minus 1.
*
*********************************************************************
*
* struct WordWrap
*
* static method getLine takes integer index returns string
*
* static method getCount takes nothing returns integer
*
* static method create takes string source, real sizeMargin, boolean preserveColors returns thistype
*
*********************************************************************
*
* Credits
*
* - cleeezzz & tooltiperror for the idea.
*
* Bugs
*
* - If there is a single word that is longer than the
* "sizeMargin" input, then this system will fail.
* It requires extra checks. I may update this later
* to fix that problem, but for now this should be fine.
* PM or message me if you have any problems.
*
*********************************************************************/
struct WordWrap extends array
private static string array line
private static integer count = 0
static method getLine takes integer index returns string
return line[index]
endmethod
static method getCount takes nothing returns integer
return count
endmethod
private static method preserveColor takes nothing returns nothing
local integer i = 0
local integer k = 0
local integer l = 0
local string temp = ""
local string hex = ""
local boolean carryOn = false
loop
exitwhen i == count
set carryOn = false
set hex = ""
set k = 0
set l = StringLength(line[i])
loop
exitwhen k == l
set temp = SubString(line[i], k, k+1)
if temp == "|" then
set temp = SubString(line[i], k+1, k+2)
if temp == "c" then
set hex = SubString(line[i], k, k+10)
set carryOn = true
elseif temp == "r" then
set carryOn = false
endif
endif
set k = k + 1
endloop
set i = i + 1
if carryOn then
set line[i] = hex + line[i]
endif
endloop
endmethod
static method create takes string source, real size, boolean preserveColors returns thistype
local integer l = StringLength(source)
local integer i = 0
local integer k = 0
local string s
local string word
local real result = 0
local real ssize = 0
loop
exitwhen count == 0
set count = count - 1
set line[count] = ""
endloop
loop
exitwhen i == l
set s = SubString(source, i, i+1)
if s == " " or i == (l-1) then
set word = SubString(source, k, i) + s
set ssize = MeasureString(word)
set result = result + ssize
if result <= size then
set line[count] = line[count] + word
else
set count = count + 1
set line[count] = word
set result = ssize
endif
set k = i + 1
endif
set i = i + 1
endloop
set count = count + 1
if preserveColors then
call ForForce(bj_FORCE_PLAYER[0], function thistype.preserveColor)
endif
return 0
endmethod
endstruct
function WordWrapString takes string source, real sizeMargin, boolean preserveColors returns nothing
call WordWrap.create(source, sizeMargin, preserveColors)
endfunction
function GetWrappedStringFragment takes integer i returns string
return WordWrap.getLine(i)
endfunction
function GetWrappedStringCount takes nothing returns integer
return WordWrap.getCount()
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//**
//* Credits:
//* - Vexorian ( ARGB )
//* - Nestharus ( ErrorMessage )
//* - Bribe ( Table )
//* - PitzerMike ( original TextSplat system )
//* - Deaod ( TextSplat system of the second generation )
//**
//* Written by BPower
library TextSplat2 /*v2.0
*************************************************************************************
*
* Creates objects of type "textsplat", which are strings displayed in a sequence of images.
* In API textsplats have a remarkable resemblance to the texttag handle.
* Only the creation of a textsplat requires an extra argument ( a font ).
*
*************************************************************************************
*
* */ requires /*
*
* */ ARGB /* wc3c.net/showthread.php?t=101858
* */ Font /* hiveworkshop.com/forums/submissions-414/textsplat2-273717/
* */ Table /* hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
* */ Queue /* github.com/nestharus/JASS/blob/master/jass/Data%20Structures/Queue/script.j
* */ optional ImageUtils /* wc3c.net/showthread.php?t=107707
* */ optional ImageTools /* hiveworkshop.com/forums/submissions-414/imagetools-271099/
* */ optional ErrorMessage /* github.com/nestharus/JASS/tree/master/jass/Systems/ErrorMessage
*
************************************************************************************/
//**
//* Import instruction:
//* ===================
//* Copy the TextSplat2, Font and their requirements into your map.
//**
//* API:
//* ====
//* The API is similar to the texttag API with one exception.
//* "CreateTextsplat(font)" expects a font as argument, while CreateTextTag() doesn't require any argument.
//* Below the object orientated API is listed, as it offers some cool feature texttags don't have.
//! novjass ( Disables the compiler until the next endnovjass )
struct textsplat
//**
//* Methods:
//* ========
static method create takes font f returns textsplat
method destroy takes nothing returns nothing
method setText takes string s, real height, integer aligntype returns nothing
//* Available aligntype are TEXTSPLAT_TEXT_ALIGN_LEFT, TEXTSPLAT_TEXT_ALIGN_CENTER and TEXTSPLAT_TEXT_ALIGN_RIGHT
//*
//* Hint: You can bind images into a text ( if added to the font ) by using "|i" as identifier.
//* Example: "The price is 2000 |igold|i"
//* Output: Textsplat will translate "|igold|i" into the gold coin image.
method setVelocity takes real xvel, real yvel, real zvel returns nothing
method setColor takes integer red, integer green, integer blue, integer alpha returns nothing
method setPosition takes real x, real y, real z return nothing
method setPosUnit takes unit u, real z returns nothing
method setSuspended takes boolean flag returns nothing
method setVisible takes boolean flag returns nothing
//**
//* Fields you may manipulate directly:
//* ===================================
font fontType
real age
real lifespan
real fadepoint
boolean permanent
//**
//* Fields you may read:
//* ====================
readonly integer charCount
//* Position values.
readonly real x
readonly real y
readonly real z
//* Velocity values.
readonly real dX
readonly real dY
readonly real dZ
//* Width and height of a text are very useful.
readonly real width
readonly real height
readonly boolean suspended
readonly boolean visible
readonly string text
readonly ARGB color
//! endnovjass
//**
//* User configuration:
//* ===================
globals
//* Set the timer accuracy.
private constant real ACCURACY = 1./32.
//* Set the default color for textsplats. If unsure use 0xFFFFFFFF.
private constant integer DEFAULT_COLOR = 0xFFFFFFFF
//* Set the default image type for textsplats.
private constant integer DEFAULT_IMAGE_TYPE = IMAGE_TYPE_SELECTION
//* Set how many chars can be parsed per function evaluation.
//* Use lower values if you use many |cAARRGGBB strings in textsplats. Maximum working value is about 65.
private constant integer TOKENS_PER_CHUNK = 40
//**
//* Globals you can read:
//* =====================
//* ( Do not change these global values )
constant integer DEFAULT_IMAGE_SIZE = 32
constant integer TEXTSPLAT_TEXT_ALIGN_LEFT = 0
constant integer TEXTSPLAT_TEXT_ALIGN_CENTER = 1
constant integer TEXTSPLAT_TEXT_ALIGN_RIGHT = 2
constant real TEXT_SIZE_TO_IMAGE_SIZE = 4.146479
endglobals
//========================================================================
//* Textsplat2 system code. Make changes carefully.
//========================================================================
//**
//* Terrain offset z:
//* =================
//* Native SetImageConstantHeight() expects an absolute z value.
globals
private constant location LOC = Location(0, 0)
endglobals
private function GetLocZ takes real x, real y returns real
call MoveLocation(LOC, x, y)
return GetLocationZ(LOC)
endfunction
//**
//* Struct Char:
//* ============
//* The data struture is a queue. You can replace the module
//* with any data structure module which supports
//* create(), destroy(), enqueue() and clear()
private struct Char extends array
implement Queue
//* Struct members.
image img
integer imageType
string path
real sizeX
real sizeY
real x
real y
real z
boolean show
boolean colorize//* Non char images are not colorized.
integer alpha
integer red
integer blue
integer green
private method draw takes nothing returns nothing
if (img != null) then
call ReleaseImage(img)
endif
static if LIBRARY_ImageTools then
set img = NewImage(path, sizeX, sizeY, x, y, 0, imageType)
elseif LIBRARY_ImageUtils then
set img = NewImage(path, sizeX, sizeY, 0, x, y, 0, 0, 0, 0, imageType)
endif
call SetImageConstantHeight(img, true, z + GetLocZ(x, y))
call SetImageColor(img, red, green, blue, alpha)
call SetImageRenderAlways(img, show)
endmethod
method setColor takes ARGB value returns nothing
set alpha = value.alpha
set red = value.red
set green = value.green
set blue = value.blue
call SetImageColor(img, red, green, blue, alpha)
endmethod
private method new takes string strPath, real sX, real sY, real posX, real posY, real posZ returns thistype
local ARGB val = DEFAULT_COLOR
set this = enqueue()//* New node.
set path = strPath
set sizeX = sX
set sizeY = sY
set x = posX
set y = posY
set z = posZ
set show = true
set imageType = DEFAULT_IMAGE_TYPE
set alpha = val.alpha
set red = val.red
set green = val.green
set blue = val.blue
call draw()
return this
endmethod
method createChar takes font ft, string symbol, real x, real y, real z, real size returns thistype
return new(ft.getChar(symbol).path, size*TEXT_SIZE_TO_IMAGE_SIZE, size*TEXT_SIZE_TO_IMAGE_SIZE, x, y, z)
endmethod
method createImage takes font ft, string img, real x, real y, real z, real size returns thistype
return new(ft.getImage(img).path, size*TEXT_SIZE_TO_IMAGE_SIZE, size*TEXT_SIZE_TO_IMAGE_SIZE, x, y, z)
endmethod
endstruct
globals
//* Globals to transfer variable values between functions, which use
//* either function.evaluate or ForForce to not hit the OOP limit.
private integer strLength
private integer currentLength
private integer tokenAmount
private real maxLineWidth
private integer currentLine
private boolean defaultColor
private ARGB currentColor
private real sourceX
private real sourceY
//*
private string array textChars
private real array lineWidth
endglobals
//**
//* Hex to Decimal:
//* ===============
globals
private Table Hex2Dec
private Table IsHex
endglobals
private function IsHexadecimal takes integer i returns boolean
local integer l = i + 8
loop
exitwhen (i >= l)
if not IsHex.has(StringHash(textChars[i])) then
return false
endif
set i = i + 1
endloop
return true
endfunction
//**
//* Perpare source string:
//* ======================
private function SplitString takes string text returns nothing
local integer i = 0
loop
exitwhen (i == strLength)
set textChars[i] = SubString(text, i, i + 1)
set i = i + 1
endloop
set textChars[i] = null//* Will indicate the end of the text.
endfunction
//**
//* String parsing:
//* ===============
globals
private constant integer TOKEN_TYPE_INVALID = 0
private constant integer TOKEN_TYPE_NORMAL = 1
private constant integer TOKEN_TYPE_NEWLINE = 2
private constant integer TOKEN_TYPE_COLOR = 3
private constant integer TOKEN_TYPE_COLOREND = 4
private constant integer TOKEN_TYPE_IMAGE = 5
endglobals
private struct TextToken extends array
static thistype current
integer tokenType
string value
ARGB color
boolean isDefaultColor
integer line
real posX
real posY
endstruct
private function TokenizeText takes nothing returns nothing
local integer i = currentLength
local TextToken base = TextToken.current
local TextToken token = base
local string char
loop
exitwhen (integer(token) - integer(base) >= TOKENS_PER_CHUNK) or (i >= strLength)
set char = textChars[i]
//* Check for \n or \r\n or |n
if (char == "\n") or (char == "\r" and textChars[i + 1] == "\n") or (char=="|" and textChars[i + 1]=="n") then
set token.tokenType = TOKEN_TYPE_NEWLINE
if (char != "\n") then
set i = i + 1
endif
//* Check for color start and color code.
elseif (char=="|") and (textChars[i + 1] == "c") and (strLength - i >= 10) and (IsHexadecimal(i + 2)) then
set token.tokenType = TOKEN_TYPE_COLOR
set token.color = ARGB.create(Hex2Dec[StringHash(textChars[i + 2] + textChars[i + 3])], Hex2Dec[StringHash(textChars[i + 4] + textChars[i + 5])], Hex2Dec[StringHash(textChars[i + 6] + textChars[i + 7])], Hex2Dec[StringHash(textChars[i + 8] + textChars[i + 9])])
set i = i + 9
//* Check for color end
elseif (char=="|") and (textChars[i + 1] == "r") then
set token.tokenType = TOKEN_TYPE_COLOREND
set i = i + 1
//* Check for image start, path and end.
elseif (char == "|") and (textChars[i + 1] == "i") then
set token.tokenType = TOKEN_TYPE_IMAGE
set token.value = ""
set i = i + 2
loop
exitwhen (textChars[i] == "|" and textChars[i + 1] == "i") or (i >= strLength)
set token.value = token.value + textChars[i]
set i = i + 1
endloop
set i = i + 1
//* Otherwise it is a normal ascii char.
else
set token.tokenType = TOKEN_TYPE_NORMAL
set token.value = char
endif
set token = token + 1
set i = i + 1
endloop
set TextToken.current = token
set currentLength = i
endfunction
private function LayoutText takes ARGB backgroundColor, font textFont, real lineHeight returns nothing
local TextToken base = TextToken.current
local TextToken token = base
local real width
loop
exitwhen (integer(token) - integer(base) >= TOKENS_PER_CHUNK) or (integer(token) >= tokenAmount)
if (token.tokenType == TOKEN_TYPE_NEWLINE) then
if (lineWidth[currentLine] > maxLineWidth) or (currentLine == 0) then
set maxLineWidth = lineWidth[currentLine]
endif
set currentLine = currentLine + 1
set lineWidth[currentLine] = 0
elseif (token.tokenType == TOKEN_TYPE_COLOR) then
set currentColor = token.color
set defaultColor = false
elseif (token.tokenType == TOKEN_TYPE_COLOREND) then
set currentColor = backgroundColor
set defaultColor = true
elseif (token.tokenType == TOKEN_TYPE_IMAGE) or (token.tokenType == TOKEN_TYPE_NORMAL) then
set token.color = currentColor
set token.isDefaultColor = defaultColor
set token.line = currentLine
if (token.tokenType == TOKEN_TYPE_IMAGE) then
set width = (textFont.getImage(token.value).width*TEXT_SIZE_TO_IMAGE_SIZE*lineHeight/DEFAULT_IMAGE_SIZE)
else
set width = (textFont.getChar(token.value).width*TEXT_SIZE_TO_IMAGE_SIZE*lineHeight/DEFAULT_IMAGE_SIZE)
endif
set token.posY = currentLine*lineHeight*TEXT_SIZE_TO_IMAGE_SIZE
set token.posX = lineWidth[currentLine]
set lineWidth[currentLine] = lineWidth[currentLine] + width
endif
set token = token + 1
endloop
set TextToken.current = token
endfunction
private function DisplayText takes real lineHeight, real bonus, real z, boolean visible, font fontType, Char chars returns nothing
local TextToken token = TextToken.current
local real x
local real y
local Char char
// call BJDebugMsg(R2S(bonus))
loop
exitwhen (integer(token) - integer(TextToken.current) >= TOKENS_PER_CHUNK) or (integer(token) >= tokenAmount)
if (token.tokenType == TOKEN_TYPE_IMAGE) or (token.tokenType == TOKEN_TYPE_NORMAL) then
set x = sourceX + token.posX + (bonus*(maxLineWidth - lineWidth[token.line]))
set y = sourceY - token.posY
if (token.tokenType == TOKEN_TYPE_IMAGE) then
set char = chars.createImage(fontType, token.value, x, y, z, lineHeight)
else
set char = chars.createChar(fontType, token.value, x, y, z, lineHeight)
endif
set char.colorize = token.isDefaultColor
call char.setColor(token.color)
set char.show = visible
call SetImageRenderAlways(char.img, visible)
set currentLength = currentLength + 1
endif
set token = token + 1
endloop
set TextToken.current = token
endfunction
//**
//* Struct textsplat:
//* =================
struct textsplat
private static constant timer TMR = CreateTimer()
//* Linked list data structure.
private static integer array next
private static integer array prev
private static boolean array inList
//**
//* Members:
//* ========
private Char chars
private integer ref
//* Fields you may read.
readonly integer charCount
readonly real x
readonly real y
readonly real z
readonly real dX
readonly real dY
readonly real dZ
readonly real width
readonly real height
readonly boolean suspended
readonly boolean visible
readonly string text
readonly ARGB color
//* Fields you may manipulate directly.
font fontType
real age
real lifespan
real fadepoint
boolean permanent
private method clear takes nothing returns nothing
local Char char = chars.first
loop
exitwhen (0 == char)
if (char.img != null) then
call ReleaseImage(char.img)
set char.img = null
endif
set char = char.next
endloop
call chars.clear()
set charCount = 0
endmethod
private method remove takes nothing returns nothing
if (inList[this]) then
set inList[this] = false
set next[prev[this]] = next[this]
set prev[next[this]] = prev[this]
if (0 == next[0]) then
call PauseTimer(TMR)
endif
endif
endmethod
method destroy takes nothing returns nothing
call clear()
call remove()
if (ref <= 0) then
call chars.destroy()
call deallocate()
endif
endmethod
method lock takes nothing returns nothing
set ref = ref + 1
endmethod
method unlock takes nothing returns nothing
set ref = ref - 1
if (ref <= 0) then
call destroy()
endif
endmethod
private static method onPeriodic takes nothing returns nothing
local thistype this = next[0]
local Char char
local boolean hasZ
local real val
loop
exitwhen (0 == this)
set char = chars.first
//* Update position.
set x = x + dX
set y = y + dY
set z = z + dZ
set hasZ = (0. != dZ)
//* Update alpha channel.
set val = 1.
if not (permanent) then
set age = age + ACCURACY
if (age >= lifespan) then
call destroy()
set char = 0//* Does not enter the loop.
elseif (age > fadepoint) then
set val = 1. - (age - fadepoint)/(lifespan - fadepoint)
endif
endif
loop
exitwhen (0 == char)
set char.x = char.x + dX
set char.y = char.y + dY
//* Unlike units, image handles may move out of WorldBounds without producing issues.
call SetImagePosition(char.img, char.x, char.y, 0.)
if (hasZ) then
set char.z = char.z + dZ
call SetImageConstantHeight(char.img, true, char.z + GetLocZ(char.x, char.y))
endif
call SetImageColor(char.img, char.red, char.green, char.blue, R2I(char.alpha*val))
set char = char.next
endloop
set this = next[this]
endloop
endmethod
private method enqueue takes nothing returns nothing
if not (inList[this]) then
set inList[this] = true
set next[this] = 0
set prev[this] = prev[0]
set next[prev[0]] = this
set prev[0] = this
if (0 == prev[this]) then
call TimerStart(TMR, ACCURACY, true, function thistype.onPeriodic)
endif
endif
endmethod
method setText takes string s, real h, integer aligntype returns nothing
set strLength = StringLength(s)
set currentLine = 0
set currentColor = color
set defaultColor = true
set maxLineWidth = 0
call clear()//* First clean the previous text.
call SplitString(s)//* Split string into single chars.
//* Tokenize.
set lineWidth[0] = 0
set currentLength = 0
set TextToken.current = 0
loop
exitwhen (currentLength >= strLength)
call ForForce(bj_FORCE_PLAYER[0], function TokenizeText)
endloop
//* Layout.
set tokenAmount = TextToken.current
set TextToken.current = 0
loop
exitwhen integer(TextToken.current) >= tokenAmount
call LayoutText.evaluate(color, fontType, h)
endloop
if (currentLine == 0) or (lineWidth[currentLine] > maxLineWidth) then
set maxLineWidth = lineWidth[currentLine]
endif
//* Display.
set sourceX = x
set sourceY = y + currentLine*h*TEXT_SIZE_TO_IMAGE_SIZE
set currentLength = 0
set TextToken.current = 0
loop
exitwhen integer(TextToken.current) >= tokenAmount
call DisplayText.evaluate(h, aligntype*.5, z, visible, fontType, chars)
endloop
set charCount = currentLength
set text = s
set width = maxLineWidth
set height = (currentLine + 1)*h*TEXT_SIZE_TO_IMAGE_SIZE
endmethod
method setVelocity takes real xvel, real yvel, real zvel returns nothing
set dX = xvel*ACCURACY
set dY = yvel*ACCURACY
set dZ = zvel*ACCURACY
endmethod
method setColor takes integer red, integer green, integer blue, integer alpha returns nothing
local Char char = chars.first
local integer val = alpha
if (age > fadepoint) and (lifespan != fadepoint) and not (permanent) then
set val = R2I((1 - (age - fadepoint)/(lifespan - fadepoint))*alpha)
endif
set color = ARGB.create(alpha, red, green, blue)
loop
exitwhen (0 == char)
if (char.colorize) then
set char.red = red
set char.green = green
set char.blue = blue
set char.alpha = alpha
call SetImageColor(char.img, red, green, blue, val)
endif
set char = char.next
endloop
endmethod
method setPosition takes real posX, real posY, real posZ returns nothing
local real newX = posX - x
local real newY = posY - y
local real newZ = posZ - z
local Char char = chars.first
loop
exitwhen (0 == char)
set char.x = char.x + newX
set char.y = char.y + newY
set char.z = char.z + newZ
call SetImagePosition(char.img, char.x, char.y, 0)
call SetImageConstantHeight(char.img, true, char.z + GetLocZ(char.x, char.y))
set char = char.next
endloop
set x = posX
set y = posY
set z = posZ
endmethod
method setPosUnit takes unit u, real z returns nothing
call setPosition(GetUnitX(u), GetUnitY(u), z)
endmethod
method setSuspended takes boolean flag returns nothing
set suspended = flag
if (flag) then
call remove()
else
call enqueue()
endif
endmethod
method setVisible takes boolean flag returns nothing
local Char char = chars.first
set visible = flag
loop
exitwhen (0 == char)
set char.show = flag
call SetImageRenderAlways(char.img, flag)
set char = char.next
endloop
endmethod
static method create takes font f returns thistype
local thistype this = thistype.allocate()
set fontType = f
set color = DEFAULT_COLOR
set visible = true
set permanent = true
set suspended = false
set chars = Char.create()//* Queue.
//* Reset position and fading members.
set dX = 0.
set dY = 0.
set dZ = 0.
set x = 0.
set y = 0.
set z = 0.
set age = 0.
set fadepoint = 0.
set lifespan = 0.
set width = 0.
set height = 0.
set ref = 0
//* Add to iterating textsplats.
call enqueue()
return this
endmethod
endstruct
function CreateTextSplat takes font fontType returns textsplat
return textsplat.create(fontType)
endfunction
function DestroyTextSplat takes textsplat t returns nothing
call t.destroy()
endfunction
function SetTextSplatFont takes textsplat t, font new returns nothing
set t.fontType = new
endfunction
function SetTextSplatAge takes textsplat t, real value returns nothing
set t.age = value
endfunction
function SetTextSplatColor takes textsplat t, integer red, integer green, integer blue, integer alpha returns nothing
call t.setColor(red, green, blue, alpha)
endfunction
function SetTextSplatFadepoint takes textsplat t, real value returns nothing
set t.fadepoint = value
endfunction
function SetTextSplatLifespan takes textsplat t, real value returns nothing
set t.lifespan = value
endfunction
function SetTextSplatPermanent takes textsplat t, boolean flag returns nothing
set t.permanent = flag
endfunction
function SetTextSplatPos takes textsplat t, real x, real y, real heightOffset returns nothing
call t.setPosition(x, y, heightOffset)
endfunction
function SetTextSplatPosUnit takes textsplat t, unit whichUnit, real heightOffset returns nothing
call t.setPosUnit(whichUnit, heightOffset)
endfunction
function SetTextSplatSuspended takes textsplat t, boolean flag returns nothing
call t.setSuspended(flag)
endfunction
function SetTextSplatText takes textsplat t, string s, real height returns nothing
call t.setText(s, height, TEXTSPLAT_TEXT_ALIGN_CENTER)
endfunction
function SetTextSplatVelocity takes textsplat t, real xvel, real yvel returns nothing
call t.setVelocity(xvel, yvel, 0)
endfunction
function SetTextSplatVisibility takes textsplat t, boolean flag returns nothing
call t.setVisible(flag)
endfunction
//**
//* Hex to Dec:
//* ===========
//! textmacro Hex2DecUpper_Macro takes L
set Hex2Dec[StringHash("$L$0")]=0x$L$0
set Hex2Dec[StringHash("$L$1")]=0x$L$1
set Hex2Dec[StringHash("$L$2")]=0x$L$2
set Hex2Dec[StringHash("$L$3")]=0x$L$3
set Hex2Dec[StringHash("$L$4")]=0x$L$4
set Hex2Dec[StringHash("$L$5")]=0x$L$5
set Hex2Dec[StringHash("$L$6")]=0x$L$6
set Hex2Dec[StringHash("$L$7")]=0x$L$7
set Hex2Dec[StringHash("$L$8")]=0x$L$8
set Hex2Dec[StringHash("$L$9")]=0x$L$9
set Hex2Dec[StringHash("$L$A")]=0x$L$A
set Hex2Dec[StringHash("$L$B")]=0x$L$B
set Hex2Dec[StringHash("$L$C")]=0x$L$C
set Hex2Dec[StringHash("$L$D")]=0x$L$D
set Hex2Dec[StringHash("$L$E")]=0x$L$E
set Hex2Dec[StringHash("$L$F")]=0x$L$F
//! endtextmacro
private module InitHex2Dec
private static method onInit takes nothing returns nothing
set Hex2Dec = Table.create()
set IsHex = Table.create()
//! runtextmacro Hex2DecUpper_Macro("0")
//! runtextmacro Hex2DecUpper_Macro("1")
//! runtextmacro Hex2DecUpper_Macro("2")
//! runtextmacro Hex2DecUpper_Macro("3")
//! runtextmacro Hex2DecUpper_Macro("4")
//! runtextmacro Hex2DecUpper_Macro("5")
//! runtextmacro Hex2DecUpper_Macro("6")
//! runtextmacro Hex2DecUpper_Macro("7")
//! runtextmacro Hex2DecUpper_Macro("8")
//! runtextmacro Hex2DecUpper_Macro("9")
//! runtextmacro Hex2DecUpper_Macro("A")
//! runtextmacro Hex2DecUpper_Macro("B")
//! runtextmacro Hex2DecUpper_Macro("C")
//! runtextmacro Hex2DecUpper_Macro("D")
//! runtextmacro Hex2DecUpper_Macro("E")
//! runtextmacro Hex2DecUpper_Macro("F")
set IsHex[StringHash("0")]=1
set IsHex[StringHash("1")]=1
set IsHex[StringHash("2")]=1
set IsHex[StringHash("3")]=1
set IsHex[StringHash("4")]=1
set IsHex[StringHash("5")]=1
set IsHex[StringHash("6")]=1
set IsHex[StringHash("7")]=1
set IsHex[StringHash("8")]=1
set IsHex[StringHash("9")]=1
set IsHex[StringHash("A")]=1
set IsHex[StringHash("B")]=1
set IsHex[StringHash("C")]=1
set IsHex[StringHash("D")]=1
set IsHex[StringHash("E")]=1
set IsHex[StringHash("F")]=1
endmethod
endmodule
private struct Inits extends array
implement InitHex2Dec
endstruct
endlibrary
library_once TextSplat uses TextSplat2
endlibrary
//TESH.scrollpos=289
//TESH.alwaysfold=0
library Table /* made by Bribe, special thanks to Vexorian & Nestharus, version 4.1.0.1.
One map, one hashtable. Welcome to NewTable 4.1.0.1
This newest iteration of Table introduces the new HashTable struct.
You can now instantiate HashTables which enables the use of large
parent and large child keys, just like a standard hashtable. Previously,
the user would have to instantiate a Table to do this on their own which -
while doable - is something the user should not have to do if I can add it
to this resource myself (especially if they are inexperienced).
This library was originally called NewTable so it didn't conflict with
the API of Table by Vexorian. However, the damage is done and it's too
late to change the library name now. To help with damage control, I
have provided an extension library called TableBC, which bridges all
the functionality of Vexorian's Table except for 2-D string arrays &
the ".flush(integer)" method. I use ".flush()" to flush a child hash-
table, because I wanted the API in NewTable to reflect the API of real
hashtables (I thought this would be more intuitive).
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 integer less = 0 //Index generation for TableArrays (below 0).
private integer more = 8190 //Index generation for Tables.
//Configure it if you use more than 8190 "key" variables in your map (this will never happen though).
private hashtable ht = InitHashtable()
private key sizeK
private key listK
endglobals
private struct dex extends array
static method operator size takes nothing returns Table
return sizeK
endmethod
static method operator list takes nothing returns Table
return listK
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
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 module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//Run these textmacros to include the entire hashtable API as wrappers.
//Don't be intimidated by the number of macros - Vexorian's map optimizer is
//supposed to kill functions which inline (all of these functions inline).
//! runtextmacro NEW_ARRAY_BASIC("Real", "Real", "real")
//! runtextmacro NEW_ARRAY_BASIC("Boolean", "Boolean", "boolean")
//! runtextmacro NEW_ARRAY_BASIC("String", "Str", "string")
//New textmacro to allow table.integer[] syntax for compatibility with textmacros that might desire it.
//! runtextmacro NEW_ARRAY_BASIC("Integer", "Integer", "integer")
//! 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 syntax (tb.handle; tb.unit; etc.)
implement realm
implement integerm
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 = tb[GetSpellAbilityId()]
method operator [] takes integer key returns Table
return LoadInteger(ht, this, key) //return this.integer[key]
endmethod
//set tb[389034] = 8192
method operator []= takes integer key, Table tb returns nothing
call SaveInteger(ht, this, key, tb) //set this.integer[key] = tb
endmethod
//set b = tb.has(2493223)
method has takes integer key returns boolean
return HaveSavedInteger(ht, this, key) //return this.integer.has(key)
endmethod
//call tb.remove(294080)
method remove takes integer key returns nothing
call RemoveSavedInteger(ht, this, key) //call this.integer.remove(key)
endmethod
//Remove all data from a Table instance
method flush takes nothing returns nothing
call FlushChildHashtable(ht, this)
endmethod
//local Table tb = Table.create()
static method create takes nothing returns Table
local Table this = dex.list[0]
if this == 0 then
set this = more + 1
set more = this
else
set dex.list[0] = dex.list[this]
call dex.list.remove(this) //Clear hashed memory
endif
debug set dex.list[this] = -1
return this
endmethod
// Removes all data from a Table instance and recycles its index.
//
// call tb.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
//! runtextmacro optional TABLE_BC_METHODS()
endstruct
//! runtextmacro optional TABLE_BC_STRUCTS()
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 tb = dex.size[array_size] //Get the unique recycle list for this array size
local TableArray this = tb[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 this = less - array_size
set less = this
else
set tb[0] = tb[this] //Set the last destroyed to the last-last destroyed
call tb.remove(this) //Clear hashed 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
//This magic method enables two-dimensional[array][syntax] for Tables,
//similar to the two-dimensional utility provided by hashtables them-
//selves.
//
//ta[integer a].unit[integer b] = unit u
//ta[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; I assume you call .flush()
//if you want it flushed too. This is a public method so that you don't
//have to loop through all TableArray indices to flush them if you don't
//need to (ie. if you were flushing all child-keys as you used them).
//
method destroy takes nothing returns nothing
local Table tb = 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 tb == 0 then
//Create a Table to index recycled instances with their array size
set tb = Table.create()
set dex.size[this.size] = tb
endif
call dex.size.remove(this) //Clear the array size from hash memory
set tb[this] = tb[0]
set tb[0] = this
endmethod
private static Table tempTable
private static integer tempEnd
//Avoids hitting the op limit
private static method clean takes nothing returns nothing
local Table tb = .tempTable
local integer end = tb + 0x1000
if end < .tempEnd then
set .tempTable = end
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
else
set end = .tempEnd
endif
loop
call tb.flush()
set tb = tb + 1
exitwhen tb == 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
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to flush an invalid TableArray instance: " + I2S(this))
debug return
debug endif
set .tempTable = this
set .tempEnd = this + this.size
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
call this.destroy()
endmethod
endstruct
//NEW: Added in Table 4.0. A fairly simple struct but allows you to do more
//than that which was previously possible.
struct HashTable extends array
//Enables myHash[parentKey][childKey] syntax.
//Basically, it creates a Table in the place of the parent key if
//it didn't already get created earlier.
method operator [] takes integer index returns Table
local Table t = Table(this)[index]
if t == 0 then
set t = Table.create()
set Table(this)[index] = t //whoops! Forgot that line. I'm out of practice!
endif
return t
endmethod
//You need to call this on each parent key that you used if you
//intend to destroy the HashTable or simply no longer need that key.
method remove takes integer index returns nothing
local Table t = Table(this)[index]
if t != 0 then
call t.destroy()
call Table(this).remove(index)
endif
endmethod
//Added in version 4.1
method has takes integer index returns boolean
return Table(this).has(index)
endmethod
//HashTables are just fancy Table indices.
method destroy takes nothing returns nothing
call Table(this).destroy()
endmethod
//Like I said above...
static method create takes nothing returns thistype
return Table.create()
endmethod
endstruct
endlibrary
//TESH.scrollpos=193
//TESH.alwaysfold=0
library Track /* v3.1.0.0
*****************************************************************************
*
* Manages trackable objects, allowing for easy event registrations, data
* retrieval, and the capability of retrieving which player interacted with
* the trackable.
*
*****************************************************************************
*
* */uses/*
*
* */ Table /* hiveworkshop.com/forums/jass-functions-413/snippet-new-table-188084/
*
*****************************************************************************
*
* SETTINGS
*/
globals
private constant integer PLATFORM = 'OTip'
endglobals
/*
*****************************************************************************
*
* FUNCTIONS
*
* CreateTrack( string modelPath, real x, real y, real z, real facing ) returns Track
* - Creates a trackable of modelPath at coordinates ( x, y, z ) with
* - "facing" in degrees. Returns the trackable instance.
*
* CreateTrackForPlayer( string modelPath, real x, real y, real z, real facing, player who ) returns Track
* - Same as function above, but creates it for one player.
*
* RegisterAnyClickEvent( code c ) returns nothing
* RegisterAnyHoverEvent( code c ) returns nothing
* - Fires "c" when any trackable is clicked or hovered.
*
* RegisterClickEvent( Track obj, code c ) returns nothing
* RegisterHoverEvent( Track obj, code c ) returns nothing
* - Fires "c" when the trackable of "obj" is clicked or hovered respectively.
* RegisterInteractEvent( Track obj, code c ) returns nothing
* - Fires "c" when the trackable of "obj" is clicked or hovered.
* - Use GetTriggerEventId() == EVENT_GAME_TRACKABLE_TRACK
* to differentiate between the two event occurrences. This
* method is more efficient on handles than the two above.
*
* EnableTrackInstance( Track obj, boolean flag ) returns nothing
* - A disabled Track instance will not fire its events.
* - Track instances are enabled by default.
* IsTrackInstanceEnabled( Track obj ) returns boolean
* - Returns whether an instance is enabled.
*
* EVENT RESPONSES
*
* GetTriggerTrackInstance() returns Track
* - Returns the Track instance that had a player interaction.
* GetTriggerTrackable() returns trackable
* - Returns the trackable object that had a player interaction.
* GetTriggerTrackablePlayer() returns player
* - Returns the player that interacted with the trackable object.
*
*****************************************************************************
*
* struct Track
*
* static Track instance
* - The triggering instance of the event.
* static trackable object
* - The triggering trackable object of the event.
* static player tracker
* - The player who interacted with the trackable object.
*
* readonly real x
* readonly real y
* readonly real z
* readonly real facing
* readonly string model
* - Instance properties.
* - Warning: the invisible platform has a default z-value
* of 2.94794. So if you input 0 into the system, it'll
* end up ~3 units above the ground. For an alternative model,
* see: [url]http://www.hiveworkshop.com/forums/2661555-post54.html[/url]
*
* method operator enabled= takes boolean flag returns nothing
* method operator enabled takes nothing returns boolean
*
* static method create takes string modelPath, real x, real y, real z, real facing returns Track
* static method createForPlayer takes string modelPath, real x, real y, real z, real facing, player p returns Track
*
* static method registerAnyClick takes code c returns nothing
* static method registerAnyHover takes code c returns nothing
*
* method registerClick takes code c returns nothing
* method registerHover takes code c returns nothing
* method registerInteract takes code c returns nothing
*
* - All equivalent to their function counterparts.
*
*****************************************************************************
*
* Credits
* - Azlier: Trackable2 (inspiration)
* - Arhowk: bugfix
* - Dalvengyr: bugfix from a typo.
* - Uberplayer: bugfix; info on the invisible platform Z issue.
*
****************************************************************************/
private module Init
private static method onInit takes nothing returns nothing
set thistype.TrackTable = Table.create()
endmethod
endmodule
struct Track extends array
private static trigger anyClick = CreateTrigger()
private static trigger anyHover = CreateTrigger()
private static Table TrackTable = 0
static thistype instance = 0
static trackable object = null
static player tracker = null
private static integer ic = 0
private static integer ir = 0
private thistype rn
readonly real x
readonly real y
readonly real z
readonly real facing
readonly string model
private trigger reg
private trigger onClick
private trigger onHover
private Table playerIndex
boolean enabled
static method registerAnyClick takes code c returns nothing
call TriggerAddCondition(.anyClick, Filter(c))
endmethod
static method registerAnyHover takes code c returns nothing
call TriggerAddCondition(.anyHover, Filter(c))
endmethod
method registerClick takes code c returns nothing
if .onClick == null then
set .onClick = CreateTrigger()
endif
call TriggerAddCondition(.onClick, Filter(c))
endmethod
method registerHover takes code c returns nothing
if .onHover == null then
set .onHover = CreateTrigger()
endif
call TriggerAddCondition(.onHover, Filter(c))
endmethod
method registerInteract takes code c returns nothing
call TriggerAddCondition(.reg, Filter(c))
endmethod
method destroy takes nothing returns nothing
call TrackTable.remove(GetHandleId(.reg))
call DestroyTrigger(.reg)
call DestroyTrigger(.onClick)
call DestroyTrigger(.onHover)
call .playerIndex.destroy()
set .rn = ir
set ir = this
endmethod
private static method onInteract takes nothing returns boolean
set instance = TrackTable[GetHandleId(GetTriggeringTrigger())]
if instance.enabled then
set object = GetTriggeringTrackable()
set tracker = Player(instance.playerIndex[GetHandleId(object)])
if GetTriggerEventId() == EVENT_GAME_TRACKABLE_TRACK then
call TriggerEvaluate(instance.onHover)
call TriggerEvaluate(anyHover)
else
call TriggerEvaluate(instance.onClick)
call TriggerEvaluate(anyClick)
endif
endif
return false
endmethod
private static method createTrack takes string modelPath, real x, real y, real z, real facing, player j returns thistype
local destructable dest = null
local thistype this = ir
local integer i = 11
local trackable tr
local player p
local string s
/* Allocate */
if this == 0 then
set ic = ic + 1
set this = ic
else
set ir = .rn
endif
/* Create platform to give the trackable a z-offset */
if z != 0 then
set dest = CreateDestructableZ(PLATFORM, x, y, z, 0, 1, 0)
endif
if j != null then
set i = GetPlayerId(j)
endif
set .x = x
set .y = y
set .z = z
set .enabled = true
set .facing = facing
set .model = modelPath
set .reg = CreateTrigger()
set .onClick = null
set .onHover = null
set .playerIndex = Table.create()
set TrackTable[GetHandleId(.reg)] = this
call TriggerAddCondition(.reg, Condition(function thistype.onInteract))
/* Create a separate trackable for each player playing */
loop
set p = Player(i)
if GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING and GetPlayerController(p) == MAP_CONTROL_USER then
if GetLocalPlayer() == p then
set s = modelPath
else
set s = ""
endif
set tr = CreateTrackable(s, .x, .y, .facing)
call TriggerRegisterTrackableHitEvent(.reg, tr)
call TriggerRegisterTrackableTrackEvent(.reg, tr)
set .playerIndex[GetHandleId(tr)] = i
exitwhen j != null
endif
exitwhen i == 0
set i = i - 1
endloop
/* Remove the platform if it exists */
if dest != null then
call RemoveDestructable(dest)
set dest = null
endif
set p = null
set tr = null
return this
endmethod
static method create takes string modelPath, real x, real y, real z, real facing returns thistype
return thistype.createTrack(modelPath, x, y, z, facing, null)
endmethod
static method createForPlayer takes string modelPath, real x, real y, real z, real facing, player p returns thistype
if not (GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING and GetPlayerController(p) == MAP_CONTROL_USER) then
return 0
endif
return thistype.createTrack(modelPath, x, y, z, facing, p)
endmethod
implement Init
endstruct
/* Function Wrappers */
function CreateTrack takes string modelPath, real x, real y, real z, real facing returns Track
return Track.create(modelPath, x, y, z, facing)
endfunction
function CreateTrackForPlayer takes string modelPath, real x, real y, real z, real facing, player who returns Track
return Track.createForPlayer(modelPath, x, y, z, facing, who)
endfunction
function EnableTrackInstance takes Track instance, boolean flag returns nothing
set instance.enabled = flag
endfunction
function IsTrackInstanceEnabled takes Track instance returns boolean
return instance.enabled
endfunction
function RegisterAnyClickEvent takes code c returns nothing
call Track.registerAnyClick(c)
endfunction
function RegisterAnyHoverEvent takes code c returns nothing
call Track.registerAnyHover(c)
endfunction
function RegisterClickEvent takes Track obj, code c returns nothing
call obj.registerClick(c)
endfunction
function RegisterHoverEvent takes Track obj, code c returns nothing
call obj.registerHover(c)
endfunction
function RegisterInteractEvent takes Track obj, code c returns nothing
call obj.registerInteract(c)
endfunction
function GetTriggerTrackInstance takes nothing returns Track
return Track.instance
endfunction
function GetTriggerTrackable takes nothing returns trackable
return Track.object
endfunction
function GetTriggerTrackablePlayer takes nothing returns player
return Track.tracker
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library ARGB initializer init
//******************************************************************************
//*
//* ARGB 1.1
//* ====
//* For your color needs.
//*
//* An ARGB object is a by-value struct, this means that assigning copies the
//* contents of the struct and that you don't have to use .destroy(), the
//* downside is that you cannot assign its members (can't do set c.r= 123 )
//*
//* This library should have plenty of uses, for example, if your spell involves
//* some unit recoloring you can allow users to input the color in the config
//* section as 0xAARRGGBB and you can then use this to decode that stuff.
//*
//* You can also easily merge two colors and make fading effects using ARGB.mix
//*
//* There's ARGB.fromPlayer which gets an ARGB object containing the player's
//* color. Then you can use the previous utilities on it.
//*
//* The .str() instance method can recolor a string, and the recolorUnit method
//* will apply the ARGB on a unit
//*
//* For other uses, you can use the .red, .green, .blue and .alpha members to get
//* an ARGB object's color value (from 0 to 255).
//*
//* structs that have a recolor method that takes red,green,blue and alpha as 0.255
//* integers can implement the ARGBrecolor module to gain an ability to quickly
//* recolor using an ARGB object.
//*
//********************************************************************************
//=================================================================================
globals
private string array i2cc
endglobals
//this double naming stuff is beginning to make me insane, if only TriggerEvaluate() wasn't so slow...
struct ARGB extends array
static method create takes integer a, integer r, integer g, integer b returns ARGB
return ARGB(b + g*0x100 + r*0x10000 + a*0x1000000)
endmethod
static method fromPlayer takes player p returns ARGB
local playercolor pc=GetPlayerColor(p)
if(pc==PLAYER_COLOR_RED) then
return 0xFFFF0303
elseif(pc==PLAYER_COLOR_BLUE) then
return 0xFF0042FF
elseif(pc==PLAYER_COLOR_CYAN) then
return 0xFF1CB619
elseif(pc==PLAYER_COLOR_PURPLE) then
return 0xFF540081
elseif(pc==PLAYER_COLOR_YELLOW) then
return 0xFFFFFF01
elseif(pc==PLAYER_COLOR_ORANGE) then
return 0xFFFE8A0E
elseif(pc==PLAYER_COLOR_GREEN) then
return 0xFF20C000
elseif(pc==PLAYER_COLOR_PINK) then
return 0xFFE55BB0
elseif(pc==PLAYER_COLOR_LIGHT_GRAY) then
return 0xFF959697
elseif(pc==PLAYER_COLOR_LIGHT_BLUE) then
return 0xFF7EBFF1
elseif(pc==PLAYER_COLOR_AQUA) then
return 0xFF106246
elseif(pc==PLAYER_COLOR_BROWN) then
return 0xFF4E2A04
endif
return 0xFF111111
endmethod
method operator alpha takes nothing returns integer
if( integer(this) <0) then
return 0x80+(-(-integer(this)+0x80000000))/0x1000000
else
return (integer(this))/0x1000000
endif
endmethod
method operator alpha= takes integer na returns ARGB
local integer a
local integer r
local integer g
local integer b
local integer col=integer(this)
if (col<0) then
set col=-(-col+0x80000000)
set a=0x80+col/0x1000000
set col=col-(a-0x80)*0x1000000
else
set a=col/0x1000000
set col=col-a*0x1000000
endif
set r=col/0x10000
set col=col-r*0x10000
set g=col/0x100
set b=col-g*0x100
return ARGB(b + g*0x100 + r*0x10000 + na*0x1000000)
endmethod
method operator red takes nothing returns integer
local integer c=integer(this)*0x100
if(c<0) then
return 0x80+(-(-c+0x80000000))/0x1000000
else
return c/0x1000000
endif
endmethod
method operator red= takes integer nr returns ARGB
local integer a
local integer r
local integer g
local integer b
local integer col=integer(this)
if (col<0) then
set col=-(-col+0x80000000)
set a=0x80+col/0x1000000
set col=col-(a-0x80)*0x1000000
else
set a=col/0x1000000
set col=col-a*0x1000000
endif
set r=col/0x10000
set col=col-r*0x10000
set g=col/0x100
set b=col-g*0x100
return ARGB(b + g*0x100 + nr*0x10000 + a*0x1000000)
endmethod
method operator green takes nothing returns integer
local integer c=integer(this)*0x10000
if(c<0) then
return 0x80+(-(-c+0x80000000))/0x1000000
else
return c/0x1000000
endif
endmethod
method operator green= takes integer ng returns ARGB
local integer a
local integer r
local integer g
local integer b
local integer col=integer(this)
if (col<0) then
set col=-(-col+0x80000000)
set a=0x80+col/0x1000000
set col=col-(a-0x80)*0x1000000
else
set a=col/0x1000000
set col=col-a*0x1000000
endif
set r=col/0x10000
set col=col-r*0x10000
set g=col/0x100
set b=col-g*0x100
return ARGB(b + ng*0x100 + r*0x10000 + a*0x1000000)
endmethod
//=======================================================
//
//
method operator blue takes nothing returns integer
local integer c=integer(this)*0x1000000
if(c<0) then
return 0x80+(-(-c+0x80000000))/0x1000000
else
return c/0x1000000
endif
endmethod
method operator blue= takes integer nb returns ARGB
local integer a
local integer r
local integer g
local integer b
local integer col=integer(this)
if (col<0) then
set col=-(-col+0x80000000)
set a=0x80+col/0x1000000
set col=col-(a-0x80)*0x1000000
else
set a=col/0x1000000
set col=col-a*0x1000000
endif
set r=col/0x10000
set col=col-r*0x10000
set g=col/0x100
set b=col-g*0x100
return ARGB(nb + g*0x100 + r*0x10000 + a*0x1000000)
endmethod
//====================================================================
// Mixes two colors, s would be a number 0<=s<=1 that determines
// the weight given to color c2.
//
// mix(c1,c2,0) = c1
// mix(c1,c2,1) = c2
// mix(c1,c2,0.5) = Mixing the colors c1 and c2 in equal proportions.
//
static method mix takes ARGB c1, ARGB c2, real s returns ARGB
//widest function ever
return ARGB( R2I(c2.blue*s+c1.blue*(1-s)+0.5) + R2I(c2.green*s+c1.green*(1-s)+0.5)*0x100 + R2I(c2.red*s+c1.red*(1-s)+0.5)*0x10000 + R2I((c2.alpha*s)+(c1.alpha*(1-s))+0.5)*0x1000000)
endmethod
method str takes string s returns string
return "|c"+i2cc[.alpha]+i2cc[.red]+i2cc[.green]+i2cc[.blue]+s+"|r"
endmethod
method recolorUnit takes unit u returns nothing
local integer a
local integer r
local integer g
local integer b
local integer col=integer(this)
if (col<0) then
set col=-(-col+0x80000000)
set a=0x80+col/0x1000000
set col=col-(a-0x80)*0x1000000
else
set a=col/0x1000000
set col=col-a*0x1000000
endif
set r=col/0x10000
set col=col-r*0x10000
set g=col/0x100
set b=col-g*0x100
call SetUnitVertexColor(u,r,g,b,a)
endmethod
endstruct
module ARGBrecolor
method ARGBrecolor takes ARGB color returns nothing
local integer a
local integer r
local integer g
local integer b
local integer col=integer(this)
if (col<0) then
set col=-(-col+0x80000000)
set a=0x80+col/0x1000000
set col=col-(a-0x80)*0x1000000
else
set a=col/0x1000000
set col=col-a*0x1000000
endif
set r=col/0x10000
set col=col-r*0x10000
set g=col/0x100
set b=col-g*0x100
call this.recolor(r, g , b, a)
endmethod
endmodule
private function init takes nothing returns nothing
local integer i=0
// Don't run textmacros you don't own!
//! textmacro ARGB_CHAR takes int, chr
set i=0
loop
exitwhen i==16
set i2cc[$int$*16+i]="$chr$"+i2cc[$int$*16+i]
set i2cc[i*16+$int$]=i2cc[i*16+$int$]+"$chr$"
set i=i+1
endloop
//! endtextmacro
//! runtextmacro ARGB_CHAR( "0","0")
//! runtextmacro ARGB_CHAR( "1","1")
//! runtextmacro ARGB_CHAR( "2","2")
//! runtextmacro ARGB_CHAR( "3","3")
//! runtextmacro ARGB_CHAR( "4","4")
//! runtextmacro ARGB_CHAR( "5","5")
//! runtextmacro ARGB_CHAR( "6","6")
//! runtextmacro ARGB_CHAR( "7","7")
//! runtextmacro ARGB_CHAR( "8","8")
//! runtextmacro ARGB_CHAR( "9","9")
//! runtextmacro ARGB_CHAR("10","A")
//! runtextmacro ARGB_CHAR("11","B")
//! runtextmacro ARGB_CHAR("12","C")
//! runtextmacro ARGB_CHAR("13","D")
//! runtextmacro ARGB_CHAR("14","E")
//! runtextmacro ARGB_CHAR("15","F")
endfunction
endlibrary
//TESH.scrollpos=123
//TESH.alwaysfold=0
library Queue /* v1.0.0.6
************************************************************************************
*
* */uses/*
*
* */ ErrorMessage /* hiveworkshop.com/forums/submissions-414/snippet-error-message-239210/
*
************************************************************************************
*
* module Queue
*
* Description
* -------------------------
*
* NA
*
* Fields
* -------------------------
*
* readonly static integer sentinel
*
* readonly thistype first
* readonly thistype next
*
* Methods
* -------------------------
*
* static method create takes nothing returns thistype
* method destroy takes nothing returns nothing
* - May only destroy queues
*
* method enqueue takes nothing returns thistype
* method pop takes nothing returns nothing
*
* method clear takes nothing returns nothing
*
* debug static method calculateMemoryUsage takes nothing returns integer
* debug static method getAllocatedMemoryAsString takes nothing returns string
*
************************************************************************************/
module Queue
private static thistype collectionCount = 0
private static thistype nodeCount = 0
debug private boolean isNode
debug private boolean isCollection
private thistype last
private thistype _next
method operator next takes nothing returns thistype
debug call ThrowError(this == 0, "Queue", "next", "thistype", this, "Attempted To Go Out Of Bounds.")
debug call ThrowError(not isNode, "Queue", "next", "thistype", this, "Attempted To Read Invalid Node.")
return _next
endmethod
private thistype _first
method operator first takes nothing returns thistype
debug call ThrowError(this == 0, "Queue", "first", "thistype", this, "Attempted To Read Null Queue.")
debug call ThrowError(not isCollection, "Queue", "first", "thistype", this, "Attempted To Read Invalid Queue.")
return _first
endmethod
static method operator sentinel takes nothing returns integer
return 0
endmethod
private static method allocateCollection takes nothing returns thistype
local thistype this = thistype(0)._first
if (0 == this) then
debug call ThrowError(collectionCount == 8191, "Queue", "allocateCollection", "thistype", 0, "Overflow.")
set this = collectionCount + 1
set collectionCount = this
else
set thistype(0)._first = _first
endif
return this
endmethod
private static method allocateNode takes nothing returns thistype
local thistype this = thistype(0)._next
if (0 == this) then
debug call ThrowError(nodeCount == 8191, "Queue", "allocateNode", "thistype", 0, "Overflow.")
set this = nodeCount + 1
set nodeCount = this
else
set thistype(0)._next = _next
endif
return this
endmethod
static method create takes nothing returns thistype
local thistype this = allocateCollection()
debug set isCollection = true
set _first = 0
return this
endmethod
method enqueue takes nothing returns thistype
local thistype node = allocateNode()
debug call ThrowError(this == 0, "Queue", "enqueue", "thistype", this, "Attempted To Enqueue On To Null Queue.")
debug call ThrowError(not isCollection, "Queue", "enqueue", "thistype", this, "Attempted To Enqueue On To Invalid Queue.")
debug set node.isNode = true
if (_first == 0) then
set _first = node
else
set last._next = node
endif
set last = node
set node._next = 0
return node
endmethod
method pop takes nothing returns nothing
local thistype node = _first
debug call ThrowError(this == 0, "Queue", "pop", "thistype", this, "Attempted To Pop Null Queue.")
debug call ThrowError(not isCollection, "Queue", "pop", "thistype", this, "Attempted To Pop Invalid Queue.")
debug call ThrowError(node == 0, "Queue", "pop", "thistype", this, "Attempted To Pop Empty Queue.")
debug set node.isNode = false
set _first = node._next
set node._next = thistype(0)._next
set thistype(0)._next = node
endmethod
method clear takes nothing returns nothing
debug local thistype node = _first
debug call ThrowError(this == 0, "Queue", "clear", "thistype", this, "Attempted To Clear Null Queue.")
debug call ThrowError(not isCollection, "Queue", "clear", "thistype", this, "Attempted To Clear Invalid Queue.")
static if DEBUG_MODE then
loop
exitwhen node == 0
set node.isNode = false
set node = node._next
endloop
endif
if (_first == 0) then
return
endif
set last._next = thistype(0)._next
set thistype(0)._next = _first
set _first = 0
endmethod
method destroy takes nothing returns nothing
debug call ThrowError(this == 0, "Queue", "destroy", "thistype", this, "Attempted To Destroy Null Queue.")
debug call ThrowError(not isCollection, "Queue", "destroy", "thistype", this, "Attempted To Destroy Invalid Queue.")
static if DEBUG_MODE then
debug call clear()
debug set isCollection = false
else
if (_first != 0) then
set last._next = thistype(0)._next
set thistype(0)._next = _first
endif
endif
set _first = thistype(0)._first
set thistype(0)._first = this
endmethod
static if DEBUG_MODE then
static method calculateMemoryUsage takes nothing returns integer
local thistype start = 1
local thistype end = 8191
local integer count = 0
loop
exitwhen integer(start) > integer(end)
if (integer(start) + 500 > integer(end)) then
return count + checkRegion(start, end)
else
set count = count + checkRegion(start, start + 500)
set start = start + 501
endif
endloop
return count
endmethod
private static method checkRegion takes thistype start, thistype end returns integer
local integer count = 0
loop
exitwhen integer(start) > integer(end)
if (start.isNode) then
set count = count + 1
endif
if (start.isCollection) then
set count = count + 1
endif
set start = start + 1
endloop
return count
endmethod
static method getAllocatedMemoryAsString takes nothing returns string
local thistype start = 1
local thistype end = 8191
local string memory = null
loop
exitwhen integer(start) > integer(end)
if (integer(start) + 500 > integer(end)) then
if (memory != null) then
set memory = memory + ", "
endif
set memory = memory + checkRegion2(start, end)
set start = end + 1
else
if (memory != null) then
set memory = memory + ", "
endif
set memory = memory + checkRegion2(start, start + 500)
set start = start + 501
endif
endloop
return memory
endmethod
private static method checkRegion2 takes thistype start, thistype end returns string
local string memory = null
loop
exitwhen integer(start) > integer(end)
if (start.isNode) then
if (memory == null) then
set memory = I2S(start)
else
set memory = memory + ", " + I2S(start) + "N"
endif
endif
if (start.isCollection) then
if (memory == null) then
set memory = I2S(start)
else
set memory = memory + ", " + I2S(start) + "C"
endif
endif
set start = start + 1
endloop
return memory
endmethod
endif
endmodule
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library TimerUtilsEx requires optional Table
/*************************************************
*
* TimerUtilsEx
* v2.1.0.2
* By Vexorian, Bribe & Magtheridon96
*
* Original version by Vexorian.
*
* Flavors:
* Hashtable:
* - RAM: Minimal
* - TimerData: Slow
*
* Array:
* - RAM: Maximal
* - TimerData: Fast
*
* All the functions have O(1) complexity.
* The Array version is the fastest, but the hashtable
* version is the safest. The Array version is still
* quite safe though, and I would recommend using it.
* The system is much slower in debug mode.
*
* Optional Requirement:
* - Table by Bribe
* - hiveworkshop.com/forums/showthread.php?t=188084
*
* API:
* ----
* - function NewTimer takes nothing returns timer
* - Returns a new timer from the stack.
* - function NewTimerEx takes integer i returns timer
* - Returns a new timer from the stack and attaches a value to it.
* - function ReleaseTimer takes timer t returns integer
* - Throws a timer back into the stack. Also returns timer data.
* - function SetTimerData takes timer t, integer value returns nothing
* - Attaches a value to a timer.
* - function GetTimerData takes timer t returns integer
* - Returns the attached value.
*
*************************************************/
// Configuration
globals
// Use hashtable, or fast array?
private constant boolean USE_HASH = false
// Max Number of Timers Held in Stack
private constant integer QUANTITY = 256
endglobals
globals
private timer array tT
private integer tN = 0
endglobals
private module Init
private static method onInit takes nothing returns nothing
static if not USE_HASH then
local integer i = QUANTITY
loop
set i = i - 1
set tT[i] = CreateTimer()
exitwhen i == 0
endloop
set tN = QUANTITY
elseif LIBRARY_Table then
set tb = Table.create()
endif
endmethod
endmodule
// JassHelper doesn't support static ifs for globals.
private struct Data extends array
static if not USE_HASH then
static integer array data
endif
static if LIBRARY_Table then
static Table tb = 0
else
static hashtable ht = InitHashtable()
endif
implement Init
endstruct
// Double free protection
private function ValidTimer takes integer i returns boolean
static if LIBRARY_Table then
return Data.tb.boolean[-i]
else
return LoadBoolean(Data.ht, i, 1)
endif
endfunction
private function Get takes integer id returns integer
debug if not ValidTimer(id) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[TimerUtils]Error: Tried to get data from invalid timer.")
debug endif
static if USE_HASH then
static if LIBRARY_Table then
return Data.tb[id]
else
return LoadInteger(Data.ht, id, 0)
endif
else
return Data.data[id - 0x100000]
endif
endfunction
private function Set takes integer id, integer data returns nothing
debug if not ValidTimer(id) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[TimerUtils]Error: Tried to attach data to invalid timer.")
debug endif
static if USE_HASH then
static if LIBRARY_Table then
set Data.tb[id] = data
else
call SaveInteger(Data.ht, id, 0, data)
endif
else
set Data.data[id - 0x100000] = data
endif
endfunction
function SetTimerData takes timer t, integer data returns nothing
call Set(GetHandleId(t), data)
endfunction
function GetTimerData takes timer t returns integer
return Get(GetHandleId(t))
endfunction
function NewTimerEx takes integer data returns timer
local integer id
if tN == 0 then
static if USE_HASH then
set tT[0] = CreateTimer()
else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[TimerUtils]Error: No Timers In The Stack! You must increase 'QUANTITY'")
return null
endif
else
set tN = tN - 1
endif
set id = GetHandleId(tT[tN])
static if LIBRARY_Table then
set Data.tb.boolean[-id] = true
else
call SaveBoolean(Data.ht, id, 1, true)
endif
call Set(id, data)
return tT[tN]
endfunction
function NewTimer takes nothing returns timer
return NewTimerEx(0)
endfunction
function ReleaseTimer takes timer t returns integer
local integer id = GetHandleId(t)
local integer data = 0
// Pause the timer just in case.
call PauseTimer(t)
// Make sure the timer is valid.
if ValidTimer(id) then
// Get the timer's data.
set data = Get(id)
// Unmark handle id as a valid timer.
static if LIBRARY_Table then
call Data.tb.boolean.remove(-id)
else
call RemoveSavedBoolean(Data.ht, id, 1)
endif
//If it's not run in USE_HASH mode, this next block is useless.
static if USE_HASH then
//At least clear hash memory while it's in the recycle stack.
static if LIBRARY_Table then
call Data.tb.remove(id)
else
call RemoveSavedInteger(Data.ht, id, 0)
endif
// If the recycle limit is reached
if tN == QUANTITY then
// then we destroy the timer.
call DestroyTimer(t)
return data
endif
endif
//Recycle the timer.
set tT[tN] = t
set tN = tN + 1
//Tried to pass a bad timer.
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[TimerUtils]Error: Tried to release non-active timer!")
endif
//Return Timer Data.
return data
endfunction
endlibrary
library TimerUtils requires TimerUtilsEx
endlibrary
//TESH.scrollpos=259
//TESH.alwaysfold=0
/***********************************************
*
* SoundTools
* v3.0.0.2
* By Magtheridon96
*
* (Special Thanks to Rising_Dusk)
*
* - Allows you to play sounds immediately after creating them.
* - Uses a sound recycler to increase efficiency and save RAM.
*
* Requirements:
* -------------
*
* - Table by Bribe
* - hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
* - TimerUtils by Vexorian
* - wc3c.net/showthread.php?t=101322
*
* API:
* ----
*
* constant boolean DEFAULT_SOUND_STOPS_ON_LEAVE_RANGE
* constant integer DEFAULT_SOUND_FADE_IN_RATE
* constant integer DEFAULT_SOUND_FADE_OUT_RATE
* constant string DEFAULT_SOUND_EAX_SETTINGS
* constant integer DEFAULT_SOUND_VOLUME
* constant integer DEFAULT_SOUND_PITCH
*
* struct Sound extends array
*
* readonly string file
* readonly integer duration
* readonly boolean looping
* readonly boolean is3D
* readonly boolean stopOnLeaveRange
* readonly integer fadeIn
* readonly integer fadeOut
* readonly string eaxSetting
*
* static method create takes string fileName, integer duration, boolean looping, boolean is3D returns thistype
* - Creates a sound struct given the filepath, the duration in milliseconds, whether it is looping or not, and whether it is 3D or not.
* static method createEx takes string fileName, integer duration, boolean looping, boolean is3D, boolean stopOnExitRange, integer fadeIn, integer fadeOut, string eaxSetting returns thistype
* - In addition to static method create, this allows you to specificy whether the sound stops when the player leaves range, the fadeIn/fadeOut rates and the EAX Setting.
* static method release takes sound s returns boolean
* - Releases a sound and throws it into the recycler. Also stops the sound.
*
* method run takes nothing returns sound
* - Plays the sound.
* method runUnit takes unit whichUnit returns sound
* - Plays the sound on a unit.
* method runPoint takes real x, real y, real z returns sound
* - Plays the sound at a point.
* method runPlayer takes player whichPlayer returns sound
* - Plays the sound for a player.
*
* method runEx takes integer volume, integer pitch returns sound
* - Plays the sound. This function allows you to pass in extra arguments.
* method runUnitEx takes unit whichUnit, integer volume, integer pitch returns sound
* - Plays the sound on a unit. This function allows you to pass in extra arguments.
* method runPointEx takes real x, real y, real z, integer volume, integer pitch returns sound
* - Plays the sound at a point. This function allows you to pass in extra arguments.
* method runPlayerEx takes player whichPlayer, integer volume, integer pitch returns sound
* - Plays the sound for a player. This function allows you to pass in extra arguments.
*
* function NewSound takes string fileName, integer duration, boolean looping, boolean is3D returns Sound
* - Creates a sound struct given the filepath, the duration in milliseconds, whether it is looping or not, and whether it is 3D or not.
* function NewSoundEx takes string fileName, integer duration, boolean looping, boolean is3D, boolean stop, integer fadeInRate, integer fadeOutRate, string eax returns Sound
* - In addition to static method create, this allows you to specificy whether the sound stops when the player leaves range, the fadeIn/fadeOut rates and the EAX Setting.
* function ReleaseSound takes sound s returns boolean
* - Releases a sound and throws it into the recycler. Also stops the sound.
* function RunSound takes Sound this returns sound
* - Plays the sound.
* function RunSoundEx takes Sound this, integer volume, integer pitch returns sound
* - Plays the sound. This function allows you to pass in extra arguments.
* function RunSoundOnUnit takes Sound this, unit whichUnit returns sound
* - Plays the sound on a unit.
* function RunSoundAtPoint takes Sound this, real x, real y, real z returns sound
* - Plays the sound at a point.
* function RunSoundForPlayer takes Sound this, player p returns sound
* - Plays the sound for a player.
* function RunSoundOnUnitEx takes Sound this, unit whichUnit, integer volume, real pitch returns sound
* - Plays the sound on a unit. This function allows you to pass in extra arguments.
* function RunSoundAtPointEx takes Sound this, real x, real y, real z, integer volume, real pitch returns sound
* - Plays the sound at a point. This function allows you to pass in extra arguments.
* function RunSoundForPlayerEx takes Sound this, player p, integer volume, real pitch returns sound
* - Plays the sound for a player. This function allows you to pass in extra arguments.
*
* Credits:
* --------
*
* - Rising_Dusk (The original system)
* - Zwiebelchen (Research - He found a ton of Wc3 sound bugs and ways to fix them)
*
***********************************************/
library SoundTools requires Table, TimerUtils
/*
* Configuration
*/
globals
constant boolean DEFAULT_SOUND_STOPS_ON_LEAVE_RANGE = true
constant integer DEFAULT_SOUND_FADE_IN_RATE = 10
constant integer DEFAULT_SOUND_FADE_OUT_RATE = 10
constant string DEFAULT_SOUND_EAX_SETTINGS = "CombatSoundsEAX"
constant integer DEFAULT_SOUND_VOLUME = 127
constant integer DEFAULT_SOUND_PITCH = 1
endglobals
globals
private constant integer SOUND_CHANNEL = 5
private constant integer SOUND_MIN_DIST = 600
private constant integer SOUND_MAX_DIST = 10000
private constant integer SOUND_DIST_CUT = 3000
endglobals
/*
* End of Configuration
*/
struct Sound extends array
private static key tk
private static key pk
private static Table tb = tk
private static Table pt = pk
private static integer index = 1
private static Table array stack
private static integer array count
readonly string file
readonly integer duration
readonly boolean looping
readonly boolean is3D
readonly boolean stopOnLeaveRange
readonly integer fadeIn
readonly integer fadeOut
readonly string eaxSetting
private real pitch
static method createEx takes string fileName, integer dur, boolean loopng, boolean isTD, boolean stop, integer fadeInRate, integer fadeOutRate, string eax returns thistype
local thistype this = index
set index = index + 1
set this.file = fileName
set this.duration = dur
set this.looping = loopng
set this.is3D = isTD
set this.stopOnLeaveRange = stop
set this.fadeIn = fadeInRate
set this.fadeOut = fadeOutRate
set this.eaxSetting = eax
set this.pitch = 1
set stack[this] = Table.create()
return this
endmethod
static method create takes string fileName, integer dur, boolean loopng, boolean isTD returns thistype
return createEx(fileName, dur, loopng, isTD, DEFAULT_SOUND_STOPS_ON_LEAVE_RANGE, DEFAULT_SOUND_FADE_IN_RATE, DEFAULT_SOUND_FADE_OUT_RATE, DEFAULT_SOUND_EAX_SETTINGS)
endmethod
// Credits to Zwiebelchen for this function
// He discovered a bug with sound pitches and this function was written to fix that.
method setSoundPitch takes sound s, real newPitch returns nothing
if GetSoundIsPlaying(s) or GetSoundIsLoading(s) then
call SetSoundPitch(s, 1/this.pitch)
call SetSoundPitch(s, newPitch)
set this.pitch = newPitch
elseif newPitch == 1 then
call SetSoundPitch(s, 1.0001)
set this.pitch = 1.0001
else
call SetSoundPitch(s, newPitch)
set this.pitch = newPitch
endif
endmethod
private static sound snd
private method get takes nothing returns sound
if count[this] == 0 then
/*
* Create new sound and point it to
* Sound struct instance.
*/
set snd = CreateSound(this.file, this.looping, this.is3D, this.stopOnLeaveRange, this.fadeIn, this.fadeOut, this.eaxSetting)
set pt[GetHandleId(snd)] = this
/*
* Configure sound
*/
call SetSoundDuration(snd, this.duration)
call SetSoundChannel(snd, SOUND_CHANNEL)
call SetSoundVolume(snd, DEFAULT_SOUND_VOLUME)
call this.setSoundPitch(snd, DEFAULT_SOUND_PITCH)
/*
* Proper 3D sound configuration
*/
if this.is3D then
call SetSoundDistances(snd, SOUND_MIN_DIST, SOUND_MAX_DIST)
call SetSoundDistanceCutoff(snd, SOUND_DIST_CUT)
call SetSoundConeAngles(snd, 0, 0, DEFAULT_SOUND_VOLUME)
call SetSoundConeOrientation(snd, 0, 0, 0)
endif
return snd
endif
/*
* Pop out of sound stack.
*/
set count[this] = count[this] - 1
return stack[this].sound[count[this]]
endmethod
private method push takes sound s returns nothing
set stack[this].sound[count[this]] = s
set count[this] = count[this] + 1
endmethod
private static method recycle takes nothing returns nothing
local timer t = GetExpiredTimer()
local sound s = tb.sound[GetHandleId(t)]
/*
* Stop sound and push to the
* stack.
*/
call StopSound(s, false, true)
call thistype(GetTimerData(t)).push(s)
call ReleaseTimer(t)
set t = null
set s = null
endmethod
private static integer array next
private static sound array media
private static method runSounds takes nothing returns nothing
local thistype this = next[0]
local timer t
call ReleaseTimer(GetExpiredTimer())
loop
exitwhen this == 0
/*
* Play the sound.
*/
call StartSound(media[this])
/*
* If it is not looping,
* we can recycle it when
* it finishes playing.
*/
if not this.looping then
set t = NewTimerEx(this)
set tb.sound[GetHandleId(t)] = media[this]
call TimerStart(t, this.duration * 0.001, false, function thistype.recycle)
endif
set media[this] = null
set this = next[this]
endloop
set next[0] = 0
set t = null
endmethod
method run takes nothing returns sound
debug if this == 0 then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[SoundTools]Error: Attempted to play null sound.")
debug return null
debug endif
if next[0] == 0 then
call TimerStart(NewTimer(), 0, false, function thistype.runSounds)
endif
if media[this] == null then
set next[this] = next[0]
set next[0] = this
set media[this] = this.get()
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[SoundTools]Warning: Attempted to run the same sound twice.")
endif
return media[this]
endmethod
method runEx takes integer volume, integer newPitch returns sound
set snd = this.run()
call SetSoundVolume(snd, volume)
call this.setSoundPitch(snd, newPitch)
return snd
endmethod
static method release takes sound s returns boolean
local integer id = GetHandleId(s)
if s == null then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[SoundTools]Error: Attempted to release a null sound.")
return false
elseif pt[id] == 0 then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[SoundTools]Error: Attempted to release a sound not allocated by RunSound.")
return false
endif
/*
* Stop the sound and push it
* to the stack.
*/
call StopSound(s, false, true)
call thistype(pt[id]).push(s)
return true
endmethod
method runUnit takes unit whichUnit returns sound
set snd = this.run()
call AttachSoundToUnit(snd, whichUnit)
return snd
endmethod
method runUnitEx takes unit whichUnit, integer volume, integer newPitch returns sound
set snd = this.runUnit(whichUnit)
call SetSoundVolume(snd, volume)
call this.setSoundPitch(snd, newPitch)
return snd
endmethod
method runPoint takes real x, real y, real z returns sound
set snd = this.run()
call SetSoundPosition(snd, x, y, z)
return snd
endmethod
method runPointEx takes real x, real y, real z, integer volume, integer newPitch returns sound
set snd = this.runPoint(x, y, z)
call SetSoundVolume(snd, volume)
call this.setSoundPitch(snd, newPitch)
return snd
endmethod
method runPlayer takes player p returns sound
set snd = this.run()
if GetLocalPlayer() != p then
call SetSoundVolume(snd, 0)
endif
return snd
endmethod
method runPlayerEx takes player p, integer volume, integer newPitch returns sound
set snd = this.runPlayer(p)
call SetSoundVolume(snd, volume)
call this.setSoundPitch(snd, newPitch)
return snd
endmethod
endstruct
function NewSoundEx takes string fileName, integer duration, boolean looping, boolean is3D, boolean stop, integer fadeInRate, integer fadeOutRate, string eax returns Sound
return Sound.createEx(fileName, duration, looping, is3D, stop, fadeInRate, fadeOutRate, eax)
endfunction
function NewSound takes string fileName, integer duration, boolean looping, boolean is3D returns Sound
return Sound.create(fileName, duration, looping, is3D)
endfunction
function RunSound takes Sound this returns sound
return this.run()
endfunction
function RunSoundEx takes Sound this, integer volume, integer pitch returns sound
return this.runEx(volume, pitch)
endfunction
function ReleaseSound takes sound s returns boolean
return Sound.release(s)
endfunction
function RunSoundOnUnit takes Sound this, unit whichUnit returns sound
return this.runUnit(whichUnit)
endfunction
function RunSoundOnUnitEx takes Sound this, unit whichUnit, integer volume, integer pitch returns sound
return this.runUnitEx(whichUnit, volume, pitch)
endfunction
function RunSoundAtPoint takes Sound this, real x, real y, real z returns sound
return this.runPoint(x, y, z)
endfunction
function RunSoundAtPointEx takes Sound this, real x, real y, real z, integer volume, integer pitch returns sound
return this.runPointEx(x, y, z, volume, pitch)
endfunction
function RunSoundForPlayer takes Sound this, player p returns sound
return this.runPlayer(p)
endfunction
function RunSoundForPlayerEx takes Sound this, player p, integer volume, integer pitch returns sound
return this.runPlayerEx(p, volume, pitch)
endfunction
endlibrary
//TESH.scrollpos=108
//TESH.alwaysfold=0
// Written by BPower.
library ImageTools /*v2.0
*************************************************************************************
*
* For your image needs.
*
* Strictly speaking it provides two wrapper function for CreateImage & DestroyImage,
* named function NewImage & function ReleaseImage.
*
* You always want to use these wrapper functions! Why so? See required know-how.
*
*************************************************************************************
*
* Required know-how [ Image extends handles for Dummies ]:
* -----------------
*
* 1. An invalid filepath crashes the game.
* 2. An invalid image type crashes the game.
* 3. DestroyImage native on an invalid image handle crashes the game.
*
* ImageTools prevents you from these fatal errors
* plus prints out debug messages, so you can quickly fix your code.
*
* Hint: A very nice image tutorial link - [url]http://www.wc3c.net/showthread.php?t=107737[/url]
*
*************************************************************************************
*
* To Deaod
* -----------------------
*
* For the original ImageUtils library ( [url]http://www.wc3c.net/showthread.php?t=107707[/url] )
*
* To Bribe
* -----------------------
*
* For Table
*
* To Vexorian
* -----------------------
*
* For ARGB
*
*************************************************************************************
*
* */ uses /*
*
* */ Table /* [url]http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/[/url]
*
************************************************************************************
*
* 1. Import instruction
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Copy the ImageTools script and library Table into your map.
* 2. API
* ¯¯¯¯¯¯
* function NewImage takes string file, real sizeX, real sizeY, real posX, real posY, real posZ, integer imageType returns image
* - Wrapper to the CreateImage native.
* - Does not crash the game, if the created image handle is invalid.
*
* function ReleaseImage takes image whichImage retuns nothing
* - Wrapper to the DestroyImage native
* - Does not crash the game if whichImage is invalid.
* - Does not crash the game if whichImage is the first ever created image in the map.
*
* function CreateImageCenter takes string file, real sizeX, real sizeY, real centerX, real centerY, real posZ, integer imageType returns image
* - Wrapper to the NewImage. It creates the image centered at centerX, centerY equal to other handle objects ( i.e. units ).
*
* 3. Configuration
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* You don't have to setup anything.
*/
globals
/**
* The following are valid image types
* sorted from highest to lowest layer:
*/
constant integer IMAGE_TYPE_SELECTION = 1// above all other image types.
constant integer IMAGE_TYPE_OCCLUSION_MASK = 3// above image type 2 and 4
constant integer IMAGE_TYPE_INDICATOR = 2// above image type 4
constant integer IMAGE_TYPE_UBERSPLAT = 4// lowest layer. Tinting affected by time of day
// and is drawn below fog of war.
/**
* Those two are image types options in GUI's "Image - Create" wrapper of CreateImageBJ.
* Both are invalid image types and should not be used in any case.
* While an image with IMAGE_TYPE_SHADOW instantly crashes the game,
* an image using IMAGE_TYPE_TOPMOST will simply not be render-able.
* The game season however will continue normally.
*/
constant integer IMAGE_TYPE_SHADOW = 0// Will create an invalid image handle with an handle id of -1.
constant integer IMAGE_TYPE_TOPMOST = 5// Will create an invalid image handle, which can't be rendered.
/**
* If an image handle is invalid, it gets the handle id -1.
* Once you are using this handle somewhere, Warcraft III crashes,
* hence ALWAYS create an image handle via function NewImage.
*/
private constant integer INVALID_IMAGE_ID = -1
/**
* When using ARGB, this is the default color set on Image.create()
*/
endglobals
static if DEBUG_MODE then
private function DebugMsg takes string s returns nothing
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "|cffff0000IMAGE UTILS ERROR:|r\n " + "|cff99b4d1" + "[" + s + "]|r" )
endfunction
endif
globals
private Table table = 0
endglobals
function NewImage takes string file, real sizeX, real sizeY, real posX, real posY, real posZ, integer imageType returns image
local image i = CreateImage(file, sizeX, sizeY, 0, posX, posY, posZ, 0, 0, 0, imageType)
if (0 > GetHandleId(i)) then
debug if (imageType < IMAGE_TYPE_SELECTION) or (imageType > IMAGE_TYPE_UBERSPLAT) then
debug call DebugMsg("function NewImage: Invalid image type [" + I2S(imageType) + "] for: " + file)
debug else
debug call DebugMsg("function NewImage: Can't find string path in data: " + file)
debug endif
return null
else
set table.boolean[GetHandleId(i)] = true
return i
endif
return i
endfunction
function ReleaseImage takes image i returns nothing
local integer id = GetHandleId(i)
if (id > 0) and (table.boolean.has(id)) then
call table.boolean.remove(id)
call DestroyImage(i)
debug elseif (id > 0) then
debug call DebugMsg("function ReleaseImage: Attempt to double destroy an image handle: [" + I2S(id) + "]!" )
debug call DebugMsg("function ReleaseImage: Or even worse, you created an image without using NewImage!" )
debug else
debug call DebugMsg("function ReleaseImage: Attempt to destroy an invalid image handle! ( null )")
endif
endfunction
function CreateImageCenter takes string file, real sizeX, real sizeY, real centerX, real centerY, real centerZ, integer imageType returns image
return NewImage(file, sizeX, sizeY, centerX - sizeX*.5, centerY - sizeY*.5, centerZ, imageType)
endfunction
private module InitImageTools
private static method onInit takes nothing returns nothing
set table = Table.create()
endmethod
endmodule
private struct I extends array
implement InitImageTools
endstruct
//! runtextmacro optional IMAGE_TOOLS_IMPORT_STRUCT_CODE()
endlibrary
//TESH.scrollpos=27
//TESH.alwaysfold=0
library ErrorMessage /* v1.0.2.0
*************************************************************************************
*
* Issue Compliant Error Messages
*
************************************************************************************
*
* function ThrowError takes boolean expression, string libraryName, string functionName, string objectName, integer objectInstance, string description returns nothing
* - In the event of an error the game will be permanently paused
*
* function ThrowWarning takes boolean expression, string libraryName, string functionName, string objectName, integer objectInstance, string description returns nothing
*
************************************************************************************/
private struct Fields extends array
static constant string COLOR_RED = "|cffff0000"
static constant string COLOR_YELLOW = "|cffffff00"
static string lastError = null
endstruct
private function Pause takes nothing returns nothing
call PauseGame(true)
endfunction
private function ThrowMessage takes string libraryName, string functionName, string objectName, integer objectInstance, string description, string errorType, string color returns nothing
local string str
local string color_braces = "|cff66FF99"
local string orange = "|cffff6600"
set str = "->\n-> " + color_braces + "{|r " + "Library" + color_braces + "(" + orange + libraryName + color_braces + ")"
if (objectName != null) then
if (objectInstance != 0) then
set str = str + "|r.Object" + color_braces + "(" + orange + objectName + color_braces + " (|rinstance = " + orange + I2S(objectInstance) + color_braces + ") )" + "|r." + "Method" + color_braces + "(" + orange + functionName + color_braces + ")"
else
set str = str + "|r.Object" + color_braces + "(" + orange + objectName + color_braces + ")|r." + "Method" + color_braces + "(" + orange + functionName + color_braces + ")"
endif
else
set str = str + "|r." + "Function" + color_braces + "(" + orange + functionName + color_braces + ")"
endif
set str = str + color_braces + " }|r " + "has thrown an exception of type " + color_braces + "(" + color + errorType + color_braces + ")|r."
set Fields.lastError = str + "\n->\n" + "-> " + color + description + "|r\n->"
endfunction
function ThrowError takes boolean expression, string libraryName, string functionName, string objectName, integer objectInstance, string description returns nothing
if (Fields.lastError != null) then
set objectInstance = 1/0
endif
if (expression) then
call ThrowMessage(libraryName, functionName, objectName, objectInstance, description, "Error", Fields.COLOR_RED)
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,Fields.lastError)
call TimerStart(CreateTimer(), 0, true, function Pause)
set objectInstance = 1/0
endif
endfunction
function ThrowWarning takes boolean expression, string libraryName, string functionName, string objectName, integer objectInstance, string description returns nothing
if (Fields.lastError != null) then
set objectInstance = 1/0
endif
if (expression) then
call ThrowMessage(libraryName, functionName, objectName, objectInstance, description, "Warning", Fields.COLOR_YELLOW)
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,Fields.lastError)
set Fields.lastError = null
endif
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library WorldBounds /* v2.0.0.0
************************************************************************************
*
* struct WorldBounds extends array
*
* Fields
* -------------------------
*
* readonly static integer maxX
* readonly static integer maxY
* readonly static integer minX
* readonly static integer minY
*
* readonly static integer centerX
* readonly static integer centerY
*
* readonly static rect world
* readonly static region worldRegion
*
************************************************************************************/
private module WorldBoundInit
private static method onInit takes nothing returns nothing
set world=GetWorldBounds()
set maxX = R2I(GetRectMaxX(world))
set maxY = R2I(GetRectMaxY(world))
set minX = R2I(GetRectMinX(world))
set minY = R2I(GetRectMinY(world))
set centerX = R2I((maxX + minX)/2)
set centerY = R2I((minY + maxY)/2)
set worldRegion = CreateRegion()
call RegionAddRect(worldRegion, world)
endmethod
endmodule
struct WorldBounds extends array
readonly static integer maxX
readonly static integer maxY
readonly static integer minX
readonly static integer minY
readonly static integer centerX
readonly static integer centerY
readonly static rect world
readonly static region worldRegion
implement WorldBoundInit
endstruct
endlibrary
//TESH.scrollpos=27
//TESH.alwaysfold=0
/**************************************************************
*
* RegisterPlayerUnitEvent
* v5.1.0.1
* By Magtheridon96
*
* I would like to give a special thanks to Bribe, azlier
* and BBQ for improving this library. For modularity, it only
* supports player unit events.
*
* Functions passed to RegisterPlayerUnitEvent must either
* return a boolean (false) or nothing. (Which is a Pro)
*
* Warning:
* --------
*
* - Don't use TriggerSleepAction inside registered code.
* - Don't destroy a trigger unless you really know what you're doing.
*
* API:
* ----
*
* - function RegisterPlayerUnitEvent takes playerunitevent whichEvent, code whichFunction returns nothing
* - Registers code that will execute when an event fires.
* - function RegisterPlayerUnitEventForPlayer takes playerunitevent whichEvent, code whichFunction, player whichPlayer returns nothing
* - Registers code that will execute when an event fires for a certain player.
* - function GetPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
* - Returns the trigger corresponding to ALL functions of a playerunitevent.
*
**************************************************************/
library RegisterPlayerUnitEvent // Special Thanks to Bribe and azlier
globals
private trigger array t
endglobals
function RegisterPlayerUnitEvent takes playerunitevent p, code c returns nothing
local integer i = GetHandleId(p)
local integer k = 15
if t[i] == null then
set t[i] = CreateTrigger()
loop
call TriggerRegisterPlayerUnitEvent(t[i], Player(k), p, null)
exitwhen k == 0
set k = k - 1
endloop
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function RegisterPlayerUnitEventForPlayer takes playerunitevent p, code c, player pl returns nothing
local integer i = 16 * GetHandleId(p) + GetPlayerId(pl)
if t[i] == null then
set t[i] = CreateTrigger()
call TriggerRegisterPlayerUnitEvent(t[i], pl, p, null)
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function GetPlayerUnitEventTrigger takes playerunitevent p returns trigger
return t[GetHandleId(p)]
endfunction
endlibrary
//TESH.scrollpos=117
//TESH.alwaysfold=0
//! novjass
//*
//* Collection of the entire UIPackage API:
//* =======================================
//*
//* Some functions have a UI_ prefix to avoid code conflicts.
//*
//* ==========================
//* Constants:
//* ==========================
//* The dummy id should match a unit using the dummy.mdx from Vexorian.
constant integer EVENT_PLAYER_UI = 1
constant real UI_MOUSE_DOUBLE_CLICK = .5
constant timer UI_STAMP = CreateTimer()
constant rect UI_RECT = Rect(0, 0, 0, 0)
constant real UI_PERFECT_SCREEN_RATIO = 1.803
constant real UI_TILE_SIZE = 64.00
//* ==========================
//* Non constants:
//* ==========================
//* UIBorder related:
//* =================
//* You can change these values, between
//* two border creations. Make sure the string paths do exist.
//* "null" and "" are considered as valid ( empty ) string path.
boolean UI_BORDER_INCLUDED_IN_RECT = false//* Frames the rect or is inside borders?
real UI_BORDER_WIDTH = 32.
ARGB UI_BORDER_BACKGROUND_COLOR = 0xFFFFFFFF
integer UI_BORDER_IMAGE_TYPE = IMAGE_TYPE_OCCLUSION_MASK
string UI_BORDER_BACKGROUND = "war3mapImported\\background.TGA"
string UI_BORDER_LEFT = "war3mapImported\\BorderLeftNE.TGA"
string UI_BORDER_RIGHT = "war3mapImported\\BorderRightNE.TGA"
string UI_BORDER_TOP = "war3mapImported\\BorderUpNE.TGA"
string UI_BORDER_BOTTOM = "war3mapImported\\BorderDownNE.TGA"
string UI_BORDER_BOTTOM_LEFT = "war3mapImported\\BorderDownLeftNE.TGA"
string UI_BORDER_BOTTOM_RIGHT = "war3mapImported\\BorderDownRightNE.TGA"
string UI_BORDER_TOP_LEFT = "war3mapImported\\BorderUpLeftNE.TGA"
string UI_BORDER_TOP_RIGHT = "war3mapImported\\BorderUpRightNE.TGA"
//* ==========================
//* library UIPackage:
//* ==========================
//*
//* List UIPackage as requirement for your custom UIs to place all UI libraries in proper compile order.
uses UIBasic, UIBorder, UIButton, UIWindow, UICamera optional UISound optional UIBoard
//*
//* ==========================
//* library UIBasic:
//* ==========================
native UnitAlive takes unit id returns boolean
//* Analogous to GetLocalPlayer(), but faster.
constant function GetLocalClient takes nothing returns player
//* Use on EVENT_PLAYER_UI_SCREEN.
constant function UI_GetTriggerPlayer takes nothing returns player
//* Properly toogles on and off unit indexing tools.
//* Returns the previous "enabled" setting of the unit indexer.
function ToogleUnitIndexer takes boolean enable returns boolean
function IsPlayerUser takes player p returns boolean
function IsPlayerComputer takes player p returns boolean
//* Registers code to UI events.
//* Outsmarts pJass. Triggerconditions can return null.
function UI_RegisterPlayerEvent takes integer id, code func returns nothing
//* Returns the previous activity setup of the indexing tool.
//* So you can disable/enable it correctly after using this function.
function ToogleUnitIndexer takes boolean enable returns boolean
//* Searches for "static method init" in the struct and calls its.
module UIInit
//* ==========================
//* library UICamera:
//* ==========================
//* UICamera API runs automatically when an UI is shown/hidden.
//* UI_StartCamera is called after EVENT_PLAYER_UI fires.
function UI_StartCamera takes player p, real x, real y, real width, real height returns nothing
//* UI_StopCamera is called before EVENT_PLAYER_UI fires.
function UI_StopCamera takes player p returns nothing
//* ==========================
//* library UIScreen:
//* ==========================
function IsPlayerInUI takes player p returns boolean
function GetPlayerUI takes player p returns UIScreen
//* New jass custom type UIScreen
//* The parent struct of all custom UIs.
struct UIScreen
readonly player user
readonly integer userId
readonly real originX
readonly real originY
readonly real width
readonly real height
readonly boolean enabled
readonly real tileSize//* by default set to 64.
//* Shows or hides a UIScreen for player "user". Fires EVENT_PLAYER_UI.
//* Only one screen per player can be shown during the same time.
method show takes boolean flag returns nothing
//* Fires after calling method show.
stub method onShow takes boolean flag returns nothing
//* UIWindow related API:
//* =====================
//* typeId is the struct id of the UIWindow.
//* We have to add windows to screens manually,
//* because JassHelper doesn't support an onCreate stub method.
//* If "autostart" is true, the window always opens when the screen is shown.
method addWindowOfType takes integer typeId, integer index, boolean autostart returns nothing
//* Get(), Has() and IsOpen()
method getWindowOfType takes integer typeId returns UIWindow
method hasWindowOfType takes integer typeId returns boolean
method isWindowOfTypeOpen takes integer typeId returns boolean
//* UIs are based on 64x64 tile size. Changing the tile size affects the scaling of all objects.
//* --> setTileSize(32.) will automatically scale down all visual objects by the factor 2.
method setTileSize takes real newSize returns nothing
//* Creator:
static method create takes player p, real x, real y, real width, real height returns UIScreen
//* Destructor: Also destroys all added UIWindow instances.
//* You can clean up you child structs via method onDestroy.
method destroy takes nothing returns nothing
//* Open() and Close(). Internal API. Don't use those two.
method openWindowOfType takes integer typeId returns boolean
method closeWindowOfType takes integer typeId returns boolean
//* ==========================
//* library UIButton:
//* ==========================
constant function UI_GetLastCreatedButton takes nothing returns UIButton
//* New jass custom type UIButton.
//* Mostly internal API. Interesting for you is the public struct member "data".
struct UIButton
//* Create a new button. Uses destructable objects.
static method createDest takes integer whichType, player p, integer objectId, Track whichTrack, real size, boolean isEnabled returns UIButton
//* Create a new button. Uses the image type.
static method createImg takes integer whichType, player p, string file, Track whichTrack, real size, boolean isEnabled returns UIButton
method destroy takes nothing returns nothing
method show takes boolean flag returns nothing
//* Adds a temporary model on the button. Can be of type image or destructable.
method addDest takes integer id, boolean showNow returns nothing
method addImage takes string file, boolean showNow returns nothing
method purge takes nothing returns nothing
//* Purges current button model and restores the original.
method remove takes nothing returns nothing
//* Get button coordinates.
method operator x takes nothing returns real
method operator y takes nothing returns real
method operator z takes nothing returns real
method operator facing takes nothing returns real
//* Get & set default button model.
method operator defaultStr takes nothing returns string
method operator defaultId takes nothing returns integer
method operator defaultStr= takes string path returns nothing
method operator defaultId= takes integer id returns nothing
//* Struct members.
readonly player user
//* typeId is the id of the struct the button was created from.
readonly integer typeId
readonly real scale
readonly Track track
readonly string tempStr
readonly integer tempId
boolean enabled
//* Example store heal ability: button.data = 'Ahea'
//* UnitAddAbility(myUnit, clicked.button.data)
integer data
//* ==========================
//* library UIBorder:
//* ==========================
//* Variables:
//* ==========
//* You can change these values, between
//* two border creations. Make sure the string paths do exist.
//* "null" and "" are considered as valid ( empty ) string path.
boolean UI_BORDER_INCLUDED_IN_RECT = false//* Frames the rect or is inside borders?
real UI_BORDER_WIDTH = 32.
ARGB UI_BORDER_BACKGROUND_COLOR = 0xFFFFFFFF
integer UI_BORDER_IMAGE_TYPE = IMAGE_TYPE_OCCLUSION_MASK
string UI_BORDER_BACKGROUND = "war3mapImported\\background.TGA"
string UI_BORDER_LEFT = "war3mapImported\\BorderLeftNE.TGA"
string UI_BORDER_RIGHT = "war3mapImported\\BorderRightNE.TGA"
string UI_BORDER_TOP = "war3mapImported\\BorderUpNE.TGA"
string UI_BORDER_BOTTOM = "war3mapImported\\BorderDownNE.TGA"
string UI_BORDER_BOTTOM_LEFT = "war3mapImported\\BorderDownLeftNE.TGA"
string UI_BORDER_BOTTOM_RIGHT = "war3mapImported\\BorderDownRightNE.TGA"
string UI_BORDER_TOP_LEFT = "war3mapImported\\BorderUpLeftNE.TGA"
string UI_BORDER_TOP_RIGHT = "war3mapImported\\BorderUpRightNE.TGA"
//* New jass custom type UIBorder.
struct UIBorder
static method create takes nothing returns UIBorder
method destroy takes nothing returns nothing
method clear takes nothing returns nothing
//* Does not desync on GetLocalPlayer() usage.
//* Is called internally when showing an UI.
method show takes boolean show returns nothing
//* Square borders build from image handles.
method construct takes real centerX, real centerY, real sizeX, real sizeY, real borderScale returns thistype
method constructRect takes rect r, real borderScale returns thistype
method addImage takes string file, real sizeX, real sizeY, real posX, real posY, integer imageType returns image
//* Same, but from destructables. *NOT CODED YET*
method constructDest takes real originX, real originY, real width, real height, real borderScale returns thistype
method constructRectDest takes rect r, real borderScale returns thistype
method addDest takes integer destId, real x, real y, real face, real scale returns destructable
//* ==========================
//* library UIWindow:
//* ==========================
//* New jass custom type UIScreen.
//* Extend your custom libraries of this struct.
//* Example: struct GameMenu extends UIScreen.
struct UIScreen
//* Coordinates of the screen.
readonly real originX
readonly real originY
readonly real width
readonly real height
method operator centerX takes nothing returns real
method operator centerY takes nothing returns real
static method create takes player user, real originX, real originY, real width, real height returns thistype
method destroy takes nothing returns nothing
//* Increases an internal counter. Starts & stops the fogmodifier when co
method operator enable= takes boolean flag returns nothing
method operator enabled takes nothing returns boolean
debug public method highlight takes nothing returns nothing
//* New jass custom type UIWindow.
//* Parent struct of all windows in a UIScreen.
//* Example: Window w in a screen s.
//* UIScreen s:
//*
//* Y-axis:
//* _______________________________
//* | 128|¯¯¯¯¯¯¯¯¯¯¯¯| |
//* | | w2 | |
//* | |¯¯¯¯¯| |____________| |
//* |128| w1 | 0/0 128 256 |
//* | |_____| |
//* | 0/0 128 |
//* |_______________________________|
//* 0/0 X - axis
struct UIWindow
//* UIWindow offers various wrappers to UIBorder, UIButton, UISound and UIBoard.
//* always use them, because they automatically inherit the coordinates of the window.
static method create takes UIScreen whichScreen, real x, real y, real sx, real sy returns thistype
method destroy takes nothing returns nothing
method show takes boolean flag returns nothing
method operator enabled takes nothing returns boolean
//* stub methods for child struct.
stub method onHover takes integer whichType, UIButton hovered returns nothing
stub method onClick takes integer whichType, UIButton clicked returns nothing
stub method onShow takes integer whichType, boolean flag returns nothing
stub method onSwitchPage takes integer whichType, boolean flag returns nothing
method isDoubleClick takes nothing returns boolean
//* Readonly members:
//* =================
readonly UIScreen screen
readonly UIBorder border
readonly Table cells
readonly real lastClick
readonly real originX
readonly real originY
readonly real width
readonly real height
//* Get window player:
//* ==================
method operator user takes nothing returns player
method operator userId takes nothing returns integer
//* Get window dimensions:
//* ======================
method operator maxX takes nothing returns real
method operator minX takes nothing returns real
method operator maxY takes nothing returns real
method operator minY takes nothing returns real
method operator centerX takes nothing returns real
method operator centerY takes nothing returns real
//* Multiple pages:
//* ===============
//* The page API is still in its infancy. I will work on it when I have time.
readonly integer pages
readonly integer page
readonly integer pageSize
//* Add() generates x copies of the first page. Can be called only once.
//* And only during the window creation process.
method addPages takes integer amount, boolean import, boolean circulate returns nothing
//* Set(), Prev(), Next()
method setPage takes integer index returns boolean
method prevPage takes nothing returns boolean
method nextPage takes nothing returns boolean
//* UIButton related:
//* =================
//* Buttons are arranged in a stack.
//* Example: getButton(2)
method getButtonCount takes nothing returns integer
method getButton takes integer index returns UIButton
//* Add(), can be destructables or images. For perfect results stay with only one type.
method addDestButton takes string model, real x, real y, real z, real face, integer objectId, real scale returns UIButton
method addImageButton takes string model, real x, real y, real z, real face, string file, real scale returns UIButton
//* Window dummy:
//* =============
//* The dummy inherits the window coordinates. Just pass in a offset in x, y and z.
method createDummy takes integer dummyId, real offsetX, real offsetY, real offsetZ, real face, real scale returns unit
//* UIBorder related:
//* =================
//* FrameWindow(), FrameWindowEx(), AddBorder(), AddImageToBorder(), AddImageToScreen()
//* Sometimes frame() doesn't perfectly match. The use frameEx to add a extra size in x and y axis.
method frame takes real borderScale returns nothing
method frameEx takes real extraX, real extraY, real borderScale returns nothing
method addBorder takes real x, real y, real sizeX,real sizeY, real borderScale returns nothing
method addBorderImage takes string file, real x, real y, real sizeX, real sizeY, integer imageType returns image
method addScreenImage takes string file, real x, real y, real sizeX, real sizeY, integer imageType returns image
//* Destructable border API will come in the future.
//* Optional API:
//* =============
method operator board takes nothing returns UIBoard
method operator audio takes nothing returns UISound
//* Debug mode only API:
//* ====================
debug method highlight takes nothing returns nothing
//* ==========================
//* library UISound: ( optional )
//* ==========================
library UISound requires SoundTools
//* Info: UISound instances are player id based.
//* In any UIWindow and UIScreen child structs:
method operator audio takes nothing returns UISound
//* New jass custom type UISound.
//* User-friendly type-casting.
struct UISound extends array
//* Runs a sound for the UI player. Only runs sounds when the previous
//* pause is over or if the priority of the next sound is higher.
method play takes Sound instance, integer priority, real pause returns boolean
//* Most common sound files.
method error takes nothing returns boolean
method bigButtonClick takes nothing returns boolean
method wayPointBling takes nothing returns boolean
method mouseClick takes nothing returns boolean
//* You can register sounds to UISound on an integer ( index ). Only positive indexes are allowed.
//* Example: UISound.register('srbd', "Sound\\Units\\Combat\\MetalMediumChopMetal1.wav", 441, 2, .2)
static method register takes integer index, string file, integer duration, integer priority, real timeout returns nothing
//* Run registered sounds via method search.
//* Example: call search('srbd')
method search takes integer index returns boolean
//* You may also search by passing the sound path.
//* Here however you have to specify priority and timeout.
method searchSoundFile takes integer index, integer priority, real timeout returns boolean
//* If you don't need a specific index, you can use allocatedIndex instead of register.
//* UISound will returns a unique integer on which the sound file is saved on.
static method allocateIndex takes string file, integer duration, integer priority, real timeout returns integer
//* ==========================
//* library UIBoard: ( optional )
//* ==========================
//* Info: UIBoard instances are player id based.
//* In any UIWindow and UIScreen child structs:
method operator board takes nothing returns UIBoard
//* New jass custom type UIBoard.
//* User-friendly type-casting.
struct UIBoard extends array
//* Readonly member:
//* ================
readonly multiboard board
//* Create() & Destroy():
//* =====================
//* Visible for only one player ( Player(this) ).
method new takes integer rows, integer columns returns thistype
//* Releases a board only if an handle exists. 100% fail-safe.
method release takes nothing returns nothing
//* Wrapper around multiboarditems:
//* ===============================
//* I recommend to use the wrappers as they prevent multiboarditems from leaking.
method setText takes integer row, integer column, string str returns nothing
method setIcon takes integer row, integer column, string str returns nothing
method setColor takes integer row, integer column, integer red, integer green, integer blue, integer alpha returns nothing
method setStyle takes integer row, integer column, boolean showValue, boolean showIcon returns nothing
method setWidth takes integer row, integer column, real width returns nothing
//! endnovjass
//TESH.scrollpos=0
//TESH.alwaysfold=0
//* Library collection:
//* ===================
//* Simply list library "UIPackage" as requirement for your custom UIs.
//* The compiler will place all required UI libraries above your script.
//* Example: - library MyCustomUI requires UIPackage.
library_once UIPackage uses UIBasic, UIBorder, UIButton, UIWindow, UICamera optional UISound optional UIBoard optional UITextbox
endlibrary
library UIBasic/* v1.0
*****************************************************************************
*
* Collection of data used all over the UI package.
*
*****************************************************************************
*
* */ uses /*
*
* */ ARGB /* wc3c.net/showthread.php?t=101858
* */ Track /* hiveworkshop.com/forums/jass-resources-412/system-track-205760/
* */ Table /* hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
* */ ImageTools /* hiveworkshop.com/forums/submissions-414/imagetools-271099/
* */ WorldBounds /* github.com/nestharus/JASS/tree/master/jass/Systems/WorldBounds
* */ ErrorMessage /* github.com/nestharus/JASS/tree/master/jass/Systems/ErrorMessage
* */ optional TileDefinition /* hiveworkshop.com/forums/jass-resources-412/snippet-tiledefinition-259347/
* */ optional WordWrap /* wc3jass.com/5016/system-wordwrap/
* */ optional TextSplat2 /* hiveworkshop.com/forums/submissions-414/textsplat2-273717/
*
***************************************************************
*/
globals
//* Public constants:
//* =================
constant real EVENT_PLAYER_UI = 1.
constant timer UI_STAMP = CreateTimer()
constant real UI_MOUSE_DOUBLE_CLICK = .5
constant rect UI_RECT = Rect(0, 0, 0, 0)
constant real UI_PERFECT_SCREEN_RATIO = 1.803
constant real UI_TILE_SIZE = 64.00
constant player UI_NEUTRAL_PLAYER = Player(15)
constant location UI_LOCATION = Location(0, 0)
//* Trackable models:
//* =================
constant string TRACK_8x8 = "war3mapImported\\8x8Track.mdx"
constant string TRACK_16x16 = "war3mapImported\\16x16Track.mdx"
constant string TRACK_32x32 = "war3mapImported\\32x32Track.mdx"
constant string TRACK_64x64 = "war3mapImported\\64x64Track.mdx"
constant string TRACK_128x128 = "war3mapImported\\128x128Track.mdx"
//* Public variables: ( UIBorder related )
//* =================
//* You can change these values, between
//* two border creations. Make sure the string paths do exist.
//* "null" and "" are considered as valid ( empty ) string path.
boolean UI_BORDER_INCLUDED_IN_RECT = false
real UI_BORDER_WIDTH = 32.
ARGB UI_BORDER_BACKGROUND_COLOR = 0xFFFFFFFF
integer UI_BORDER_IMAGE_TYPE = IMAGE_TYPE_OCCLUSION_MASK
integer UI_BORDER_BACKGROUND_IMAGE_TYPE = IMAGE_TYPE_INDICATOR
string UI_BORDER_BACKGROUND = null
string UI_BORDER_LEFT = null
string UI_BORDER_RIGHT = null
string UI_BORDER_TOP = null
string UI_BORDER_BOTTOM = null
string UI_BORDER_BOTTOM_LEFT = null
string UI_BORDER_BOTTOM_RIGHT = null
string UI_BORDER_TOP_LEFT = null
string UI_BORDER_TOP_RIGHT = null
endglobals
//* Public functions:
//* =================
native UnitAlive takes unit id returns boolean
//* Useful for creating dummy units.
function ToogleUnitIndexer takes boolean enable returns boolean
local boolean prevSetup = true
static if LIBRARY_UnitIndexer then
set prevSetup = UnitIndexer.enabled
set UnitIndexer.enabled = enable
elseif LIBRARY_UnitIndexerGUI then
set prevSetup = udg_UnitIndexerEnabled
set udg_UnitIndexerEnabled = enable
elseif LIBRARY_UnitDex then
set prevSetup = UnitDex.Enabled
set UnitDex.Enabled = enable
endif
return prevSetup
endfunction
globals
private player client = null
endglobals
//* Returns GetLocalPlayer(). Just faster.
constant function GetLocalClient takes nothing returns player
return client
endfunction
//* Returns the slot status of a player.
function IsPlayerUser takes player p returns boolean
return (GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING) and (GetPlayerController(p) == MAP_CONTROL_USER)
endfunction
function IsPlayerComputer takes player p returns boolean
return not IsPlayerUser(p)
endfunction
globals
//* Event variables:
//* ================
private integer index = 0
private real eventVal = 0.
endglobals
function UI_RegisterPlayerEvent takes code func returns nothing
local trigger trig = CreateTrigger()
call TriggerRegisterVariableEvent(trig, SCOPE_PRIVATE + "eventVal", EQUAL, EVENT_PLAYER_UI)
call TriggerAddCondition(trig, Condition(func))
set trig = null
endfunction
constant function UI_GetTriggerPlayer takes nothing returns player
return Player(index)
endfunction
//* Non public API. Do not use this function.
function UI_FireEvent takes integer pi, real ev returns nothing
local integer prev = index
set index = pi
set eventVal = ev
set eventVal = 0.
set index = prev
endfunction
function UI_GetZ takes real x, real y returns real
call MoveLocation(UI_LOCATION, x, y)
return GetLocationZ(UI_LOCATION)
endfunction
globals
private real elapsed = 0.
endglobals
function UI_GetElapsedTime takes nothing returns real
return elapsed + TimerGetElapsed(UI_STAMP)
endfunction
private function OneHour takes nothing returns nothing
set elapsed = elapsed + 3600.
endfunction
//* Makes sure everything is initialized in time.
//* Expects static method init inside the struct.
module UIInit
static if thistype.init.exists then
private static method onInit takes nothing returns nothing
call thistype.init()
endmethod
else
"Error: Can't find static method thistype.init"
endif
endmodule
private struct I extends array
private static method init takes nothing returns nothing
set client = GetLocalPlayer()
call TimerStart(UI_STAMP, 3600., true, function OneHour)
endmethod
implement UIInit
endstruct
endlibrary
//TESH.scrollpos=37
//TESH.alwaysfold=0
library UIButton uses UIBasic/* v 1.0
**************************************************************
*
* Creates buttons for full screen systems.
* Those are interactable fields within an UI.
* Buttons can store data and changed their model.
*
* Can be of type destructable or image.
*
**************************************************************
*/
globals
//* Access via UI_GetLastCreatedButton()
private integer lastCreated = 0
endglobals
struct UIButton //* [SIZE]
//* Available native types for a button:
//* ====================================
readonly destructable dest
readonly image img
readonly player user
readonly integer typeId
readonly real scale
readonly Track track
//* Button textures:
//* ================
readonly string tempStr
readonly integer tempId
private string defStr
private integer defId
method operator defaultStr takes nothing returns string
return defStr
endmethod
method operator defaultId takes nothing returns integer
return defId
endmethod
method operator defaultStr= takes string path returns nothing
set defaultId = 0
set defStr = path
endmethod
method operator defaultId= takes integer id returns nothing
set defStr = null
set defId = id
endmethod
//* Enable/Disable buttons i.e. button.enabled = false
//* Disabled buttons do not display their image handle.
boolean enabled
//* Transfer data:
//* ==============
//* There to save reference to other systems on a button.
//* i.e. store heal ability: button.data = 'Ahea'
//* UnitAddAbility(myUnit, clicked.button.data)
integer data
//* Wrappers to x, y, z and facing of the Track instance.
method operator z takes nothing returns real
return track.z + UI_GetZ(track.x, track.y)
endmethod
//! textmacro PUBLIC_UI_IMAGE_BUTTON_OPERATOR takes NAME
public method operator $NAME$ takes nothing returns real
return track.$NAME$
endmethod
//! endtextmacro
//! runtextmacro PUBLIC_UI_IMAGE_BUTTON_OPERATOR("x")
//! runtextmacro PUBLIC_UI_IMAGE_BUTTON_OPERATOR("y")
//! runtextmacro PUBLIC_UI_IMAGE_BUTTON_OPERATOR("facing")
method purge takes nothing returns nothing
if (img != null) then
call ReleaseImage(img)
set img = null
endif
if (dest != null) then
call RemoveDestructable(dest)
set dest = null
endif
endmethod
method remove takes nothing returns nothing
call purge()
set tempStr = null
set tempId = 0
if (enabled) then
if (defStr != null) and (defStr != "") then
set img = NewImage(defStr, UI_TILE_SIZE*scale, UI_TILE_SIZE*scale, x - UI_TILE_SIZE/2*scale, y - UI_TILE_SIZE/2*scale, 0, IMAGE_TYPE_OCCLUSION_MASK)
call SetImageRenderAlways(img, GetLocalClient() == user)
elseif (0 != defId) then
set dest = CreateDestructableZ(defId, x, y, z, facing, scale, 0)
call ShowDestructable(dest, GetLocalClient() == user)
endif
endif
endmethod
method addDest takes integer id, boolean flag returns nothing
call purge()
set tempId = id
if (enabled) and (flag) then
set dest = CreateDestructableZ(id, x, y, z, facing, scale, 0)
call ShowDestructable(dest, GetLocalClient() == user)
debug call ThrowWarning(GetDestructableTypeId(dest)== 0, "UIButton", "add", "id", 0, "Invalid destructable id ( 0 )")
endif
endmethod
method addImage takes string file, boolean flag returns nothing
call purge()
set tempStr = file
if (enabled) and (flag) then
set img = NewImage(file, UI_TILE_SIZE*scale, UI_TILE_SIZE*scale, x - UI_TILE_SIZE/2*scale, y - UI_TILE_SIZE/2*scale, 0, IMAGE_TYPE_OCCLUSION_MASK)
call SetImageRenderAlways(img, GetLocalClient() == user)
endif
endmethod
method show takes boolean flag returns nothing
local boolean b = (tempStr == null) and (tempId == 0)
if not (flag) then
call purge()
elseif (b) then
call remove()
elseif (tempStr != null) then
call addImage(tempStr, true)
elseif (tempId != 0) then
call addDest(tempId, true)
endif
endmethod
method destroy takes nothing returns nothing
call purge()
set user = null
set data = 0
set track = 0
call deallocate()
endmethod
static method createDest takes integer whichType, player p, integer objectId, Track whichTrack, real size, boolean isEnabled returns thistype
local thistype this = thistype.allocate()
set user = p
set defId = objectId
set defStr = null
set scale = size
set enabled = isEnabled
set typeId = whichType
set track = whichTrack
set tempId = 0
debug call ThrowError((not IsPlayerUser(p)), "UIButton", "create", "p", this, "Invalid player handle ( Computer player ); ID:[" + I2S(GetPlayerId(p)) + "]")
debug call ThrowWarning((p == null), "UIButton", "create", "p", this, "Invalid player handle ( null )!")
set lastCreated = this
return this
endmethod
static method createImg takes integer whichType, player p, string file, Track whichTrack, real size, boolean isEnabled returns thistype
local thistype this = thistype.allocate()
set user = p
set defStr = file
set defId = 0
set tempStr = null
set scale = size
set enabled = isEnabled
set typeId = whichType
set track = whichTrack
debug call ThrowWarning((p == null), "UIButton", "create", "p", this, "Invalid player handle ( null )!")
debug call ThrowError((not IsPlayerUser(p)), "UIButton", "create", "p", this, "Invalid player handle ( Computer player ); ID:[" + I2S(GetPlayerId(p)) + "]")
set lastCreated = this
return this
endmethod
endstruct
constant function UI_GetLastCreatedButton takes nothing returns UIButton
return lastCreated
endfunction
endlibrary
//TESH.scrollpos=63
//TESH.alwaysfold=0
library UIBorder uses UIBasic/* v 1.0
**************************************************************
*
* Creates borders, including a background image for full screen systems.
*
**************************************************************
*/
globals
private boolean INCLUDED = UI_BORDER_INCLUDED_IN_RECT
private real WIDTH = UI_BORDER_WIDTH
private string BACKGROUND = UI_BORDER_BACKGROUND
private string LEFT = UI_BORDER_LEFT
private string RIGHT = UI_BORDER_RIGHT
private string TOP = UI_BORDER_TOP
private string BOTTOM = UI_BORDER_BOTTOM
private string BOTTOM_LEFT = UI_BORDER_BOTTOM_LEFT
private string BOTTOM_RIGHT = UI_BORDER_BOTTOM_RIGHT
private string TOP_LEFT = UI_BORDER_TOP_LEFT
private string TOP_RIGHT = UI_BORDER_TOP_RIGHT
private ARGB BACKGROUND_COLOR = UI_BORDER_BACKGROUND_COLOR
private integer IMAGE_TYPE = UI_BORDER_IMAGE_TYPE
private integer IMAGE_TYPE_2 = UI_BORDER_BACKGROUND_IMAGE_TYPE
endglobals
private function Reset takes nothing returns nothing
set UI_BORDER_INCLUDED_IN_RECT = INCLUDED
set UI_BORDER_BACKGROUND = BACKGROUND
set UI_BORDER_WIDTH = WIDTH
set UI_BORDER_LEFT = LEFT
set UI_BORDER_RIGHT = RIGHT
set UI_BORDER_TOP = TOP
set UI_BORDER_BOTTOM = BOTTOM
set UI_BORDER_BOTTOM_LEFT = BOTTOM_LEFT
set UI_BORDER_BOTTOM_RIGHT = BOTTOM_RIGHT
set UI_BORDER_TOP_LEFT = TOP_LEFT
set UI_BORDER_TOP_RIGHT = TOP_RIGHT
set UI_BORDER_BACKGROUND_IMAGE_TYPE = IMAGE_TYPE_2
set UI_BORDER_BACKGROUND_COLOR = BACKGROUND_COLOR
set UI_BORDER_IMAGE_TYPE = IMAGE_TYPE
endfunction
globals
private constant integer OFFSET = 0x186A0//* 100000
private constant real RATIO = (UI_TILE_SIZE + 2.)/UI_TILE_SIZE
endglobals
struct UIBorder
private Table stack
private method operator sizeDest= takes integer value returns nothing
set stack[-2] = value
endmethod
public method operator sizeDest takes nothing returns integer
return stack[-2]
endmethod
private method operator size= takes integer value returns nothing
set stack[-1] = value
endmethod
public method operator size takes nothing returns integer
return stack[-1]
endmethod
method clear takes nothing returns nothing
local integer i = 0
local integer max = size
loop
exitwhen i == max
call ReleaseImage(stack.image[i])
set i = i + 1
endloop
set size = 0
endmethod
method destroy takes nothing returns nothing
call clear()
call stack.destroy()
call deallocate()
endmethod
method setColor takes integer red, integer green, integer blue, integer alpha returns nothing
local integer i = 0
local integer max = size
loop
exitwhen i == max
call SetImageColor(stack.image[i], red, green, blue, alpha)
set i = i + 1
endloop
endmethod
//* Use GetLocalPlayer or IsPlayerInForce.
method show takes boolean show returns nothing
local integer i = 0
local integer max = size
loop
exitwhen i == max
call SetImageRenderAlways(stack.image[i], show)
set i = i + 1
endloop
endmethod
//* Image type API:
//* ===============
method addImage takes string file, real sizeX, real sizeY, real posX, real posY, integer imageType returns image
set stack.image[size] = NewImage(file, sizeX, sizeY, posX, posY, 0, imageType)
set size = size + 1
return stack.image[size - 1]
endmethod
//* Code taken from Custom Window by Maker:
//* Refer to: hiveworkshop.com/forums/spells-569/customwindow-v1-1-1-2-a-207526/?prev=search%3Dwindow%26d%3Dlist%26r%3D20
method construct takes real centerX, real centerY, real sizeX, real sizeY, real borderScale returns thistype
//! textmacro UI_WINDOW_BORDER_NEW takes MODEL, SIZEX, SIZEY, CENTERX, CENTERY
if (UI_BORDER_$MODEL$ != "") then
set stack.image[index] = NewImage(UI_BORDER_$MODEL$, $SIZEX$, $SIZEY$, $CENTERX$, $CENTERY$, 0, UI_BORDER_IMAGE_TYPE)
set index = index + 1
endif
//! endtextmacro
local integer index = size
local integer i = 0
local integer nx = 0 // x pieces
local integer ny = 0 // y pieces
local real sx = 0
local real sy = 0
local real cx = 0
local real cy = 0
local real sxa = 0
local real sya = 0
local real bwidth = UI_BORDER_WIDTH*borderScale
local real x = UI_TILE_SIZE*borderScale
local real y = UI_TILE_SIZE*borderScale
local ARGB color = UI_BORDER_BACKGROUND_COLOR
if not UI_BORDER_INCLUDED_IN_RECT then
set sizeX = sizeX + bwidth*2
set sizeY = sizeY + bwidth*2
endif
if (sizeX < 2*x) then
set sizeX = 2*x
endif
set sx = x*RATIO
set nx = R2I((sizeX - 2*x)/x)
if (nx == 0) and (((sizeX - 2*x)/x) != 0.) then
set nx = 1
endif
if (nx != 0) then
set sxa = (sizeX - 2*x)/(nx*x)*sx
else
set sxa = 1
endif
set cx = centerX - sizeX/2 + x - borderScale
if (sizeY < 2*y) then
set sizeY = 2*y
endif
set sy = y*RATIO
set ny = R2I((sizeY - 2*y)/y)
if (ny == 0) and (((sizeY - 2*y)/y) != 0.) then
set ny = 1
endif
if (ny != 0) then
set sya = (sizeY - 2*x)/(ny*x)*sy
else
set sya = 1
endif
set cy = centerY - sizeY/2 + y - borderScale
// 1. Background image
if (UI_BORDER_BACKGROUND != "") and (UI_BORDER_BACKGROUND != null) then
set stack.image[index] = NewImage(UI_BORDER_BACKGROUND, sizeX - bwidth, sizeY - bwidth, centerX - (sizeX - bwidth)*.5, centerY - (sizeY - bwidth)*.5, 0, UI_BORDER_BACKGROUND_IMAGE_TYPE)
call SetImageColor(stack.image[index], color.red, color.green, color.blue, color.alpha)
set index = index + 1
endif
set centerX = centerX - borderScale
set centerY = centerY - borderScale
// 2. Corner images.
//! runtextmacro UI_WINDOW_BORDER_NEW("BOTTOM_LEFT", "sx", "sy", "centerX - sizeX*0.5", "centerY - sizeY*0.5")
//! runtextmacro UI_WINDOW_BORDER_NEW("TOP_LEFT", "sx", "sy", "centerX - sizeX*0.5", "centerY + sizeY*0.5 - sy/RATIO")
//! runtextmacro UI_WINDOW_BORDER_NEW("TOP_RIGHT", "sx", "sy", "centerX + sizeX*0.5 - x", "centerY + sizeY*0.5 - sy/RATIO")
//! runtextmacro UI_WINDOW_BORDER_NEW("BOTTOM_RIGHT", "sx", "sy", "centerX + sizeX*0.5 - x", "centerY - sizeY*0.5")
// 3. Upwards and downwards.
set i = 0
loop
exitwhen i == nx
//! runtextmacro UI_WINDOW_BORDER_NEW("TOP", "sxa", "sy", "cx", "centerY + sizeY*0.5 - y")
//! runtextmacro UI_WINDOW_BORDER_NEW("BOTTOM", "sxa", "sy", "cx", "centerY - sizeY*0.5")
set cx = cx + sxa/RATIO
set i = i + 1
endloop
// 4. Left and right
set i = 0
loop
exitwhen i == ny
//! runtextmacro UI_WINDOW_BORDER_NEW("LEFT", "sx", "sya", "centerX - sizeX*0.5", "cy")
//! runtextmacro UI_WINDOW_BORDER_NEW("RIGHT", "sx", "sya", "centerX + sizeX*0.5 - x", "cy")
set cy = cy + sya/RATIO
set i = i + 1
endloop
set size = index
call Reset()
return this
endmethod
//* The construct method using center coordinates is rock solid.
//* This wrapper exists for those who prefer an API analogous to the image API.
method constructEx takes real originX, real originY, real sizeX, real sizeY, real borderScale returns thistype
return construct(originX + sizeX*.5, originY + sizeY*.5, sizeX, sizeY, borderScale)
endmethod
method constructRect takes rect r, real borderScale returns thistype
return construct(GetRectCenterX(r), GetRectCenterY(r), GetRectMaxX(r) - GetRectMinX(r), GetRectMaxY(r) - GetRectMinY(r), borderScale)
endmethod
static method create takes nothing returns thistype
local thistype this = thistype.allocate()
set stack = Table.create()
set size = 0
return this
endmethod
//* Destructable type API:
//* ======================
//* Not yet coded.
method addDest takes integer destId, real x, real y, real z, real face, real scale returns destructable
set stack.destructable[sizeDest + OFFSET] = CreateDestructableZ(destId, x, y, z, face, scale, 0)
set sizeDest = sizeDest + 1
return stack.destructable[sizeDest + OFFSET - 1]
endmethod
method constructDest takes real originX, real originY, real width, real height, real borderScale returns thistype
debug call ThrowWarning(true, "UIBorder", "constructDest", "-", this, "This method is not yet coded in this version of UIPackage!")
return this
endmethod
method constructRectDest takes rect r, real borderScale returns thistype
return constructDest(GetRectMinX(r), GetRectMinY(r), GetRectMaxX(r) - GetRectMinX(r), GetRectMaxY(r) - GetRectMinY(r), borderScale)
endmethod
endstruct
endlibrary
//TESH.scrollpos=149
//TESH.alwaysfold=0
library UIScreen uses UIBasic, UICamera optional UIBoard optional UISound/* v1.1
*****************************************************************************
*
* Each custom UI is build of one UIScreen having a unlimited amount of UIWindows.
* Struct UIScreen is the parent struct you base your UIs on.
*
* Realistic example: CustomInventory extends UIScreen.
*
****************************************************************************
*/
globals
private integer array current
endglobals
//* Parent struct of all custom UIs.
struct UIScreen
//* System safety:
//* ==============
method operator exists takes nothing returns boolean
return (user != null)
endmethod
//* Stub method:
//* ============
//* Fires after calling method show.
stub method onShow takes boolean flag returns nothing
endmethod
//* Readonly members:
//* =================
readonly player user
readonly real tileSize
readonly real originX
readonly real originY
readonly real width
readonly real height
readonly boolean enabled
method setTileSize takes real newSize returns nothing
set tileSize = newSize
endmethod
method operator scale takes nothing returns real
return tileSize/UI_TILE_SIZE
endmethod
method operator centerX takes nothing returns real
return originX + (width/2)
endmethod
method operator centerY takes nothing returns real
return originY + (height/2)
endmethod
//* User-friendly. Operator overloading.
method operator userId takes nothing returns integer
debug call ThrowError((user == null), "UIScreen", "userId", "user", this, "Attempt access a deallocated instance!")
return GetPlayerId(user)
endmethod
//* private members:
//* ================
private Table windows
method openWindowOfType takes integer typeId returns boolean
if (enabled) and (windows.has(-typeId)) and not windows.boolean.has(-typeId) then
set windows.boolean[-typeId] = true
return true
endif
return false
endmethod
method closeWindowOfType takes integer typeId returns boolean
if (windows.boolean.has(-typeId)) then
call windows.boolean.remove(-typeId)
return true
endif
return false
endmethod
//* UIWindow related API:
//* Get(), Has(), IsOpen() and Add()
method hasWindowOfType takes integer typeId returns boolean
debug call ThrowError((user == null), "UIScreen", "hasWindowOfType", "user", this, "Attempt access a deallocated instance!")
return windows.has(-typeId)
endmethod
method getWindowOfType takes integer typeId returns UIWindow
debug call ThrowError((user == null), "UIScreen", "getWindowOfType", "user", this, "Attempt access a deallocated instance!")
return windows[-typeId]
endmethod
method isWindowOfTypeOpen takes integer typeId returns boolean
debug call ThrowError((user == null), "UIScreen", "isWindowOfTypeOpen", "user", this, "Attempt access a deallocated instance!")
return windows.boolean.has(-typeId)
endmethod
method addWindowOfType takes integer typeId, integer index, boolean autostart returns nothing
debug call ThrowError(windows.has(-typeId), "UIScreen", "addWindowOfType", "typeId", this, "UIWindow already added!")
set windows.boolean[typeId] = autostart
set windows[-typeId] = index
set windows[typeId] = windows[0]
set windows[0] = typeId
endmethod
//* Optional getters:
//* =================
static if LIBRARY_UIBoard then
method operator board takes nothing returns UIBoard
return userId
endmethod
endif
static if LIBRARY_UISound then
method operator audio takes nothing returns UISound
return userId
endmethod
endif
//* Not so nice, but required.
private method autoShowWindows takes boolean flag returns nothing
local integer index = windows[0]
loop
exitwhen index == 0
if not (flag) and windows.boolean.has(-index) then
call UIWindow.recursiveShow.execute(windows[-index], flag)
elseif (flag) and (windows.boolean[index]) then
call UIWindow.recursiveShow.execute(windows[-index], flag)
endif
set index = windows[index]
endloop
endmethod
method show takes boolean flag returns nothing
local thistype prev = current[userId]
if (flag) and not (enabled) then
//* Close previously seen screens first.
if (0 != prev) then
call prev.show(false)
endif
set enabled = true
set current[userId] = this
if GetLocalClient() == user then
call FogEnable(false)
call FogMaskEnable(false)
endif
call autoShowWindows(true)
call onShow(flag)
call UI_StartCamera(user, originX, originY, width, height)
call UI_FireEvent(userId, EVENT_PLAYER_UI)
elseif not (flag) and (enabled) then
set enabled = false
call autoShowWindows(false)
if GetLocalClient() == user then
call FogEnable(true)
call FogMaskEnable(true)
endif
call UI_StopCamera(user)
call onShow(flag)
static if LIBRARY_UIBoard then
call board.release()
endif
set current[userId] = 0
call UI_FireEvent(userId, EVENT_PLAYER_UI)
endif
endmethod
static method create takes player p, real x, real y, real sx, real sy returns thistype
debug local string msg = GetPlayerName(p) + " is a computer player! Only non-computer players can see an UIWindow!"
debug local boolean error1 = (x + sx > WorldBounds.maxX) or (x < WorldBounds.minX)
debug local boolean error2 = (y + sy > WorldBounds.maxY) or (y < WorldBounds.minY)
//**
local thistype this = allocate()
set user = p
set tileSize = UI_TILE_SIZE
set windows = Table.create()
set originX = x
set originY = y
set width = sx
set height = sy
set enabled = false
//* DEBUG_MODE:
//* ===========
debug call ThrowError(IsPlayerComputer(p), "UIScreen", "create", "user", this, msg)
debug call ThrowError(error1, "UIScreen", "create", "error1", this, "X coordinates of the UI screen are out of WorldBounds!")
debug call ThrowError(error2, "UIScreen", "create", "error2", this, "Y coordinates of the UI screen are out of WorldBounds!")
return this
endmethod
private method destroyWindows takes nothing returns nothing
local integer index = windows[0]
loop
exitwhen index == 0
call UIWindow.recursiveDestroy.execute(windows[-index])
set index = windows[index]
endloop
endmethod
method destroy takes nothing returns nothing
call ThrowError(not exists, "UIScreen", "destroy", "not exists", this, "Attempt to destroy an invalid UI Screen!")
if (enabled) then
debug call ThrowWarning(enabled, "UIScreen", "destroy", "enabled", this, "Attempt to destroy an UI screen, which is yet enabled!")
call show(false)
endif
call deallocate()
call destroyWindows()
call windows.destroy()
set user = null
endmethod
//* Only available in debug mode. Highlights the borders of your screen.
//* So you can check ingame, that you settings are correct.
debug public method highlight takes nothing returns nothing
debug local string flash = "DRAB"
debug call AddLightning(flash, false, originX, originY, originX, originY + height)
debug call AddLightning(flash, false, originX, originY + height, originX + width, originY + height)
debug call AddLightning(flash, false, originX + width, originY, originX, originY)
debug call AddLightning(flash, false, originX + width, originY + height, originX + width, originY)
debug endmethod
endstruct
function IsPlayerInUI takes player p returns boolean
return (current[GetPlayerId(p)] != 0)
endfunction
function GetPlayerUI takes player p returns UIScreen
return current[GetPlayerId(p)]
endfunction
endlibrary
//TESH.scrollpos=128
//TESH.alwaysfold=0
//* Written by BPower.
library UICamera initializer Init/* v1.0
*************************************************************************************
*
* Adjusts the camera setup while viewing a UIScreen.
*
*************************************************************************************
*
* */ uses /*
*
* */ UIBasic /*
*
************************************************************************************
*
* Import instruction:
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Copy the UICamera script into your map.
*/
globals
//* Constants:
//* ==========
private constant timer TIMER = CreateTimer()
private constant real UPDATE_CAMERA = (1./32.)
constant real UI_CAMERA_SCREEN_WIDTH = 1.632//* Empirical values.
constant real UI_CAMERA_SCREEN_HEIGHT = 0.906
endglobals
globals
//* Data structure:
//* ===============
private integer array stack
private integer size = 0
//* Variables:
//* ==========
private camerasetup array camera
private real array resetX
private real array resetY
private real array update
endglobals
private function SetCameraTargetDistance takes integer index, real width, real height, real time returns nothing
local real d = RMaxBJ(width*UI_CAMERA_SCREEN_HEIGHT, height*UI_CAMERA_SCREEN_WIDTH)
call CameraSetupSetField(camera[index], CAMERA_FIELD_TARGET_DISTANCE, d, time)
endfunction
//* The stack has a lookup speed of O(n).
//* This is utterly acceptable as the maximum
//* stack size is bj_MAX_PLAYERS, which equals 12.
private function Get takes player p returns integer
local integer index = 0
local integer id = GetPlayerId(p)
loop
exitwhen (index == size)
if (id == stack[index]) then
return index
endif
set index = index + 1
endloop
return -1
endfunction
private function OnPeriodic takes nothing returns nothing
local integer index = 0
local integer id
loop
exitwhen index == size
set id = stack[index]
if GetLocalClient() == Player(id) then
call CameraSetupApplyForceDuration(camera[id], true, 0.)
endif
set index = index + 1
endloop
endfunction
//* Push to stack:
//* ==============
private function Push takes integer id returns nothing
if (0 == size) then
call TimerStart(TIMER, UPDATE_CAMERA, true, function OnPeriodic)
endif
set stack[size] = id
set size = size + 1
//* Camera position natives have a internal delay of ~0.05.
//* In multiplayer seasons it's probably more.
//* We don't not want to end up between to windows.
if (update[id] < TimerGetElapsed(UI_STAMP) - 0.5) then
//* Always returns coordinates for a local client.
set resetX[id] = GetCameraTargetPositionX()
set resetY[id] = GetCameraTargetPositionY()
endif
endfunction
function UI_StopCamera takes player p returns nothing
local integer index = Get(p)
local integer id = GetPlayerId(p)
if (-1 != index) then
set size = size - 1
set stack[index] = stack[size]
if (0 == size) then
call PauseTimer(TIMER)
endif
if (GetLocalClient() == p) then
call SetCameraBoundsToRect(bj_mapInitialCameraBounds)
call CameraSetSmoothingFactor(1.)
call ResetToGameCamera(0.)
call SetCameraPosition(resetX[id], resetY[id])
endif
//* Bug fix:
//* ========
set update[id] = TimerGetElapsed(UI_STAMP)
//* Only in DEBUG_MODE:
debug else
debug call ThrowWarning((true), "UICamera", "UI_StopCamera", "index", -1, GetPlayerName(p) + " was not found in the stack!")
endif
endfunction
function UI_StartCamera takes player p, real originX, real originY, real width, real height returns nothing
local integer id = GetPlayerId(p)
local real x = originX + width*.5
local real y = originY + height*.5
if (IsPlayerUser(p)) then
//* If the player is not in the stack
//* function Get will return -1.
if (-1 == Get(p)) then
call Push(id)
endif
//* Set camerasetup:
//* ================
call SetCameraTargetDistance(id, width, height, 0.)
if (GetLocalClient() == p) then
call SetCameraBounds(x, y, x, y, x, y, x, y)
call CameraSetSmoothingFactor(0.)
call CameraSetupApplyForceDuration(camera[id], true, 0.)
endif
//* Only in DEBUG_MODE:
debug else
debug call ThrowWarning(true, "UICamera", "UI_StartCamera", "p", 0, GetPlayerName(p) + " is a computer player! Only non-computer players are valid for UI_StartCamera!")
endif
endfunction
private function Init takes nothing returns nothing
local integer index = 0
local boolean prev
loop
exitwhen (index == bj_MAX_PLAYERS)
if IsPlayerUser(Player(index)) then
set camera[index] = CreateCameraSetup()
call CameraSetupSetField(camera[index], CAMERA_FIELD_ANGLE_OF_ATTACK, 270., 0.)
call CameraSetupSetField(camera[index], CAMERA_FIELD_ROLL, 0., 0.)
call CameraSetupSetField(camera[index], CAMERA_FIELD_ROTATION, 90., 0.)
endif
set index = index + 1
endloop
endfunction
endlibrary
//TESH.scrollpos=461
//TESH.alwaysfold=0
library UIWindow uses UIButton, UIBorder, UIBasic, UIScreen/* v1.0
*****************************************************************************
*
* Each custom UI is build of one UIScreen having a unlimited amount of UIWindows.
* Struct UIWindow is the parent struct you base your windows in an UIScreen on.
*
* Realistic example:
* struct Inventory extends UIScreen
* struct Backpack extends UIWindow.
* struct Equipmenet extends UIWindow ...
*
****************************************************************************
*/
globals
//* Required room for data storage:
//* ===============================
private constant hashtable TABLE = InitHashtable()
private constant hashtable TRACKER = InitHashtable()
endglobals
//* Parent struct of all custom windows.
struct UIWindow
//* Stub methods:
//* =============
public stub method onHover takes UIScreen whichScreen, UIButton hovered returns nothing
endmethod
public stub method onClick takes UIScreen whichScreen, UIButton clicked returns nothing
endmethod
public stub method preShow takes UIScreen whichScreen returns boolean
return true
endmethod
public stub method onShow takes UIScreen whichScreen, boolean flag returns nothing
endmethod
public stub method onShowPage takes UIScreen whichScreen, boolean flag returns nothing
endmethod
//* Readonly members:
//* =================
readonly UIScreen screen
readonly UIBorder border
readonly Table cells
readonly real lastClick
readonly real originX
readonly real originY
readonly real width
readonly real height
//* I honestly don't know how this method performs on Battle.net
//* In single player games it works flawless. ( latency? )
method isDoubleClick takes nothing returns boolean
return (UI_GetElapsedTime() - lastClick < UI_MOUSE_DOUBLE_CLICK)
endmethod
//* Get the amount of buttons in a window.
readonly static constant integer TABLE_SIZE = -1
method getButtonCount takes nothing returns integer
return cells[TABLE_SIZE]
endmethod
//* User-friendly type-casting.
method getButton takes integer index returns UIButton
debug call ThrowWarning(not cells.has(index), "UIWindow", "getButton", "index", this, "No UIButton saved on this index [" + I2S(index) + "]!")
return cells[index]
endmethod
method operator enabled takes nothing returns boolean
return screen.isWindowOfTypeOpen(getType())
endmethod
//* User-friendly getters.
method operator user takes nothing returns player
return screen.user
endmethod
method operator userId takes nothing returns integer
return screen.userId
endmethod
//* Get window dimensions:
//* ======================
//! textmacro UI_WINDOW_SIZE_OPERATOR takes NAME, SUFFIX, EXTRA
public method operator $NAME$$SUFFIX$ takes nothing returns real
return origin$SUFFIX$ $EXTRA$
endmethod
//! endtextmacro
//! runtextmacro optional UI_WINDOW_SIZE_OPERATOR("max", "X", "+ width")
//! runtextmacro optional UI_WINDOW_SIZE_OPERATOR("max", "Y", "+ height")
//! runtextmacro optional UI_WINDOW_SIZE_OPERATOR("min", "X", "")
//! runtextmacro optional UI_WINDOW_SIZE_OPERATOR("min", "Y", "")
//! runtextmacro optional UI_WINDOW_SIZE_OPERATOR("center", "X", "+ width*.5")
//! runtextmacro optional UI_WINDOW_SIZE_OPERATOR("center", "Y", "+ height*.5")
//* Multiple pages:
//* ===============
//* The page API is still in its infancy.
//* I will work on it when I have time.
readonly integer pages
readonly integer page
readonly integer pageSize
private boolean circle//* When nextPage() hits the maximum, should we go back to page 1? ( same for prevPage() )
debug private boolean invalid
//* Optional getters:
//* =================
//* Optional getters:
//* =================
static if LIBRARY_UIBoard then
method operator board takes nothing returns UIBoard
return screen.userId
endmethod
endif
static if LIBRARY_UISound then
method operator audio takes nothing returns UISound
return screen.userId
endmethod
endif
static if LIBRARY_UITextbox and LIBRARY_Font then
method operator textbox takes nothing returns UITextbox
return screen.userId
endmethod
endif
//* Window Dummy:
//* =============
//* The dummy inherits the window coordinates. Just pass in a offset.
method createDummy takes player p, integer dummyId, real offsetX, real offsetY, real offsetZ, real face, real scale returns unit
local boolean prev = ToogleUnitIndexer(false)
set bj_lastCreatedUnit = CreateUnit(p, dummyId, originX + offsetX, originY + offsetY, 1)
call ToogleUnitIndexer(prev)
call SetUnitFacing(bj_lastCreatedUnit, face + 1.)
call SetUnitFacing(bj_lastCreatedUnit, face)
call SetUnitScale(bj_lastCreatedUnit, scale*screen.scale, 0., 0.)
call UnitAddAbility(bj_lastCreatedUnit, 'Aloc')
call UnitAddAbility(bj_lastCreatedUnit, 'Amrf')
call UnitRemoveAbility(bj_lastCreatedUnit, 'Amrf')
call SetUnitFlyHeight(bj_lastCreatedUnit, offsetZ, 0.)
call SetUnitX(bj_lastCreatedUnit, originX + offsetX)
call SetUnitY(bj_lastCreatedUnit, originY + offsetY)
if (GetLocalClient() != user) then
call SetUnitVertexColor(bj_lastCreatedUnit, 0, 0, 0, 0)// More safe then hide/show unit for a local client.
endif
call PauseUnit(bj_lastCreatedUnit, true)
return bj_lastCreatedUnit
endmethod
//* Pages API:
//* ==========
//* Set(), Next(), Prev() ... all return a boolean for success.
private method showPage takes boolean flag returns nothing
local UIButton b
local integer i = pageSize*(page - 1)
local integer size = i + pageSize
loop
exitwhen (i == size)
set b = getButton(i)
if (b.enabled != flag) then
set b.enabled = flag
call b.show(flag)
endif
set i = i + 1
endloop
call onShowPage(screen, flag)
endmethod
method setPage takes integer index returns boolean
if not (enabled) or (index > pages) or (index == page) or (index < 1) then
return false
endif
call showPage(false)
set page = index
call showPage(true)
return true
endmethod
method prevPage takes nothing returns boolean
local integer prev = page - 1
if (pages == 1) or not (enabled) then
return false
elseif (prev < 1) then
if not (circle) then
return false
endif
set prev = pages
endif
call showPage(false)
set page = prev
call showPage(true)
return true
endmethod
method nextPage takes nothing returns boolean
local integer next = page + 1
if (pages == 1) or not (enabled) then
return false
elseif (next > pages) then
if not (circle) then
return false
endif
set next = 1
endif
call showPage(false)
set page = next
call showPage(true)
return true
endmethod
//* Pages are copies of the first page. API may change in the future.
//* They also can inherit Button data if you want.
private static thistype var = 0
private static boolean trans = false
private static method newPage takes nothing returns nothing
local thistype this = thistype.var
local boolean import = thistype.trans
local integer index = 0
local integer size = cells[thistype.TABLE_SIZE]
local UIButton copy = 0
set pages = pages + 1
loop
exitwhen index == pageSize
set copy = getButton(index)
if (copy.defaultId == 0) then
set cells[size] = UIButton.createImg(copy.typeId, user, copy.defaultStr, copy.track, copy.scale, false)
else
set cells[size] = UIButton.createDest(copy.typeId, user, copy.defaultId, copy.track, copy.scale, false)
endif
if (import) then
set UIButton(cells[size]).data = copy.data
endif
set size = size +1
set index = index + 1
endloop
set cells[thistype.TABLE_SIZE] = size
endmethod
//* Pages API:
//* ==========
//* Add() generates x copies of the first page.
method addPages takes integer amount, boolean import, boolean circulate returns nothing
debug call ThrowError((amount <= 0), "UIWindow", "addPages", "amount", this, "Inavlid argument ( amount <= 0 )!")
debug call ThrowError((amount >= 10), "UIWindow", "addPages", "amount", this, "The maximum number of allowed pages is 9!")
debug call ThrowError((invalid), "UIWindow", "addPages", "invalid", this, "Additional Pages Can Only Be Set Once!")
debug set invalid = true
set pageSize = cells[thistype.TABLE_SIZE]
set circle = circulate
loop
exitwhen 0 >= amount
set thistype.var = this
set thistype.trans = import
call ExecuteFunc(thistype.newPage.name)
set amount = amount - 1
endloop
endmethod
//* Show a window:
//* ==============
method show takes boolean flag returns nothing
if (this == 0) then
return
endif
if (flag) and (GetPlayerUI(screen.user) == screen) and preShow(screen) then
if (screen.openWindowOfType(getType())) then
set page = 1
else
return
endif
elseif not (flag) then
if not (screen.closeWindowOfType(getType())) then
return
endif
else
return
endif
if (GetLocalClient() == screen.user) then
call border.show(flag)
endif
call onShow(screen, flag)
call showPage(flag)
endmethod
//**
//* Track search & save:
//* ====================
//*
//* Trackables are poorly supported in Warcraft III. In fact we can't
//* remove handles of type "trackable" once they are no longer used.
//* Therefore I came up with an algorithm to search out of "all ever created Tracks in UIWindows"
//* these which match 100% to the one we wish to create and use that Track instance instead.
//*
//* library TileDefinition creates unique integers per tile in your map.
//* Tracks are saved in stacks on the tile id in an hashtable.
//* With an 64x64 track model the loop will exit after (1 - 3) * players loops.
//* For bigger model it will be 1 * players, for smallers the loopup time will increase.
//*
//* Due to an hashtable lookup during both trackable events ( hover & click ),
//* a Track instance can evaluate any UIWindow onHover/onClick stub method without
//* running into code collision with other UIs which use the same Track instance.
//* Credits to IcemanBo:
//* ====================
static if not LIBRARY_TileDefinition then
private static integer WorldTiles_X = 0
private static integer WorldTiles_Y = 0
private static method setVars takes nothing returns nothing
set WorldTiles_X = R2I(WorldBounds.maxX - WorldBounds.minX) / 128 + 1
set WorldTiles_Y = R2I(WorldBounds.maxY - WorldBounds.minY) / 128 + 1
endmethod
private static method GetTileId takes real x, real y returns integer
local integer xI = R2I(x - WorldBounds.minX + 64) / 128
local integer yI = R2I(y - WorldBounds.minY + 64) / 128
if ((xI < 0) or (xI >= .WorldTiles_X) or (yI < 0) or (yI >= .WorldTiles_Y)) then
return -1
endif
return (yI * .WorldTiles_X + xI)
endmethod
endif
private method saveTrack takes Track track returns Track
local integer id = GetTileId(track.x, track.y)
local integer size = LoadInteger(TRACKER, id, 0) + 1
call SaveInteger(TRACKER, id, size, track)
call SaveInteger(TRACKER, id, -size, userId)
call SaveInteger(TRACKER, id, 0, size)
return track
endmethod
private method searchTrack takes string model, real x, real y, real z, real face returns Track
local integer id = GetTileId(x, y)
local integer size = LoadInteger(TRACKER, id, 0)
local Track t
loop
exitwhen 0 == size
if (LoadInteger(TRACKER, id, -size) == userId) then
set t = LoadInteger(TRACKER, id, size)
if (t.x == x) and (t.y == y) and (t.z == z) and (t.facing == face) and (t.model == model) then
return t
endif
endif
set size = size - 1
endloop
return saveTrack(Track.createForPlayer(model, x, y, z, face, user))
endmethod
//* UIButton API:
//* =============
//* Adds an UIButton to your custom UI. The function uses a search & save algorithm for Tracks
//* ( read above ) to optimize the trackable handle count in your map.
method addDestButton takes string model, real x, real y, real z, real face, integer objectId, real scale returns UIButton
local integer typeId = getType()
local integer size = getButtonCount()
local Track track = searchTrack(model, originX + x, originY + y, z, face)
debug call ThrowError(invalid, "UIWindow", "addButton", "invalid", this, "Can't add further UIButtons, once method addPages is called!")
set cells[size] = UIButton.createDest(typeId, user, objectId, track, scale*screen.scale, false)
set cells[TABLE_SIZE]= size + 1
set pageSize = size + 1
call SaveInteger(TABLE, screen, -track, this)
call SaveInteger(TABLE, screen, track, size)
return cells[size]
endmethod
method addImageButton takes string model, real x, real y, real z, real face, string file, real scale returns UIButton
local integer typeId = getType()
local integer size = getButtonCount()
local Track track = searchTrack(model, originX + x, originY + y, z, face)
debug call ThrowError(invalid, "UIWindow", "addButton", "invalid", this, "Can't add further UIButtons, once method addPages is called!")
set cells[size] = UIButton.createImg(typeId, user, file, track, scale*screen.scale, false)
set cells[TABLE_SIZE]= size + 1
set pageSize = size + 1
call SaveInteger(TABLE, screen, -track, this)
call SaveInteger(TABLE, screen, track, size)
return cells[size]
endmethod
//* Wrapper methods to ease the usage of UIBorder. ( adds originX / originY to the desired coordinates )
method frame takes real borderScale returns nothing
call border.construct(originX + width*.5, originY + height*.5, width, height, borderScale*screen.scale)
endmethod
method frameEx takes real extraX, real extraY, real borderScale returns nothing
call border.construct(originX + width*.5, originY + height*.5, width + extraX, height + extraY, borderScale*screen.scale)
endmethod
method addBorder takes real x, real y, real sizeX,real sizeY, real borderScale returns nothing
call border.construct(originX + x + sizeX*.5, originY + y + sizeY*.5, sizeX, sizeY, borderScale*screen.scale)
endmethod
method addBorderImage takes string file, real x, real y, real sizeX, real sizeY, integer imageType returns image
return border.addImage(file, sizeX, sizeY, originX + x, originY + y, imageType)
endmethod
method addScreenImage takes string file, real x, real y, real sizeX, real sizeY, integer imageType returns image
return border.addImage(file, sizeX, sizeY, screen.originX + x, screen.originY + y, imageType)
endmethod
//* Creates a new UIWindow for the player allocated in the UIScreen argument!
static method create takes UIScreen whichScreen, real x, real y, real sx, real sy returns thistype
debug local boolean error1 = (x + sx > whichScreen.width)
debug local boolean error2 = (y + sy > whichScreen.height)
debug local boolean error3 = (x < 0.)
debug local boolean error4 = (y < 0.)
local thistype this = thistype.allocate()
set cells = Table.create()
set originX = whichScreen.originX + x
set originY = whichScreen.originY + y
set width = sx
set height = sy
set screen = whichScreen
set border = UIBorder.create()
set pages = 1
set page = 1
set pageSize = 0
debug set invalid = false
debug call ThrowError(error1, "UIWindow", "create", "error1", this, "X coordinates of the window are out of the screen bounds [" + R2S(x + sx) + "]!")
debug call ThrowError(error2, "UIWindow", "create", "error2", this, "Y coordinates of the window are out of the screen bounds [" + R2S(y + sy) + "]!")
debug call ThrowError(error3, "UIWindow", "create", "error3", this, "X coordinates of the window are out of the screen bounds [" + R2S(x) + "]!")
debug call ThrowError(error4, "UIWindow", "create", "error4", this, "Y coordinates of the window are out of the screen bounds [" + R2S(y) + "]!")
return this
endmethod
//* Trackable events:
//* =================
private static method onAnyHover takes nothing returns boolean
local UIScreen UI = GetPlayerUI(Track.tracker)
local thistype this = LoadInteger(TABLE, UI, -Track.instance)
local integer index = LoadInteger(TABLE, UI, Track.instance) + (pageSize*(page - 1))
local UIButton hovered = cells[index]
if (this != 0) and (hovered.enabled) then
call onHover(UI, hovered)
endif
return false
endmethod
private static method onAnyClick takes nothing returns boolean
local UIScreen UI = GetPlayerUI(Track.tracker)
local thistype this = LoadInteger(TABLE, UI, -Track.instance)
local integer index = LoadInteger(TABLE, UI, Track.instance) + (pageSize*(page - 1))
local UIButton clicked = cells[index]
if (0 != this) and (clicked.enabled) then
call onClick(UI, clicked)
set lastClick = UI_GetElapsedTime()
endif
return false
endmethod
//* Trackables remain! As clean as possible.
method destroy takes nothing returns nothing
local integer index = 0
local integer size = getButtonCount()
call deallocate()
loop
exitwhen index == size
call getButton(index).destroy()
set index = index + 1
endloop
call cells.destroy()
call border.destroy()
endmethod
//* Executed from UIScreen:
//* =======================
//* via function.execute()
static method recursiveShow takes thistype this, boolean flag returns nothing
call show(flag)
endmethod
static method recursiveDestroy takes thistype this returns nothing
call destroy()
endmethod
private static method init takes nothing returns nothing
call Track.registerAnyClick(function thistype.onAnyClick)
call Track.registerAnyHover(function thistype.onAnyHover)
static if not LIBRARY_TileDefinition then
call thistype.setVars()
endif
endmethod
implement UIInit
//* Only available in debug mode. Highlights the borders of your screen.
//* So you can check ingame, that you settings are correct.
debug public method highlight takes nothing returns nothing
debug local string flash = "MFPB"
debug call AddLightning(flash, false, originX, originY, originX, originY + height)
debug call AddLightning(flash, false, originX, originY + height, originX + width, originY + height)
debug call AddLightning(flash, false, originX + width, originY, originX, originY)
debug call AddLightning(flash, false, originX + width, originY + height, originX + width, originY)
debug endmethod
endstruct
endlibrary
//TESH.scrollpos=102
//TESH.alwaysfold=0
library UISound /*v1.0
*************************************************************************************
*
* For your sound needs in custom UIs.
* Instances are player id based. You can directly access them in any UIWindow struct.
*
* --> method operator audio takes nothing returns UISound
*
* Example: call this.audio.play(file, 1, 0.2)
*
* UISound must use the "Sound" type created by library SoundTools.
*
*************************************************************************************
*
* */ uses /*
*
* */ UIBasic /*
* */ SoundTools /* http://www.hiveworkshop.com/forums/jass-resources-412/system-soundtools-207308/
*
************************************************************************************
*
* 1. Import instruction
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Copy the UISound script into you map.
* 2. API
* ¯¯¯¯¯¯
* method play takes Sound instance, integer priority, real pause returns boolean
* - Create a native sound handle from string ( file ).
* - pause: the time no new sound of the same or lower priority can be played.
* - priority: overrides the previous sound or blocks next sound handles.
*
*/
globals
private Table table = 0
private Table save = 0
private Sound mouseClickS = 0
private Sound errorS = 0
private Sound bigButtonClickS = 0
private Sound wayPointBlingS = 0
private integer index = 0
endglobals
struct UISound extends array
private static sound temp
private real last
private integer prior
private sound snd
private real active
private method condition takes integer priority, real pause returns boolean
local real elapsed = UI_GetElapsedTime()
if (priority > prior) or (last <= elapsed) then
set prior = priority
set last = elapsed + pause
return true
endif
return false
endmethod
public method play takes Sound instance, integer priority, real pause returns sound
if (instance != 0) and condition(priority, pause) then
set snd = instance.run()
if GetLocalClient() != Player(this) then
call SetSoundVolume(snd, 0)
endif
return snd
endif
return null
endmethod
public method searchSoundFile takes string file, integer priority, real timeout returns sound
local integer index = StringHash(file)
if save.has(index) then
return play(save[index], priority, timeout)
endif
return null
endmethod
public method search takes integer index returns sound
if table.has(index) then
return play(table[index], table[-index], table.real[index])
endif
return null
endmethod
public method error takes nothing returns sound
return play(errorS, 100, .1)
endmethod
public method bigButtonClick takes nothing returns sound
return play(bigButtonClickS, 1, .1)
endmethod
public method wayPointBling takes nothing returns sound
return play(wayPointBlingS, 1, .1)
endmethod
public method mouseClick takes nothing returns sound
return play(mouseClickS, 1, .2)
endmethod
private static method init takes nothing returns nothing
set table = Table.create()
set save = Table.create()
set mouseClickS = Sound.create("Sound\\Interface\\MouseClick1.wav", 239, false, false)
set errorS = Sound.create("Sound\\Interface\\Error.wav", 641, false, false)
set bigButtonClickS = Sound.create("Sound\\Interface\\BigButtonClick.wav", 390, false, false)
set wayPointBlingS = Sound.create("Sound\\Interface\\WayPointBling.wav", 361, false, false)
endmethod
implement UIInit
public static method register takes integer index, string file, integer duration, integer priority, real timeout returns nothing
local integer hashed = StringHash(file)
debug call ThrowWarning(table.has(index), "UISound", "register", "index", index, "This index is already taken!")
if save.has(hashed) then
set table[index] = save[hashed]
else
set table[index] = Sound.createEx(file, duration, false, false, false, 10, 10, "CombatSoundsEAX")
set save[hashed] = table[index]
endif
set table.real[index] = RMinBJ(timeout, duration/1000.)
set table[-index] = priority
endmethod
public static method allocateIndex takes string file, integer duration, integer priority, real timeout returns integer
set index = index -1
call register(index, file, duration, priority, timeout)
return index
endmethod
endstruct
endlibrary
//TESH.scrollpos=9
//TESH.alwaysfold=0
library UIBoard/*v1.0
*************************************************************************************
*
* For your multiboard needs in custom UIs.
*
* UIBoard uses player id based instances and offers
* wrappers around the "multiboarditem" handle to prevent them from leaking.
* UIBoards are associated to UIScreens and automatically destroyed when you close them.
*
*************************************************************************************
*
* */ uses /*
*
* */ UIBasic /*
*
**************************************************************************************
*/
struct UIBoard extends array
//* Readonly member:
//* ================
readonly multiboard board
//* UIBoard API:
//* ============
method release takes nothing returns nothing
if (board != null) then
call DestroyMultiboard(board)
set board = null
endif
endmethod
//* Visible for only one player.
method new takes integer rows, integer columns returns thistype
call release()
set board = CreateMultiboard()
call MultiboardSetColumnCount(board, columns)
call MultiboardSetRowCount(board, rows)
call MultiboardDisplay(board, GetLocalClient() == Player(this))
call MultiboardMinimize(board, false)
call MultiboardSetItemsStyle(board, true, false)
return this
endmethod
//* Wrapper around multiboarditems:
//* ===============================
method setRowWidth takes integer row, real width returns nothing
local integer index = 0
local integer columns = MultiboardGetColumnCount(board)
local multiboarditem i = null
loop
exitwhen (index > columns) or (columns == 0)
set i = MultiboardGetItem(board, row, columns)
call MultiboardSetItemWidth(i, width)
call MultiboardReleaseItem(i)
set index = index + 1
endloop
set i = null
endmethod
method setColumnWidth takes integer column, real width returns nothing
local integer index = 0
local integer rows = MultiboardGetRowCount(board)
local multiboarditem i = null
loop
exitwhen (index > rows) or (rows == 0)
set i = MultiboardGetItem(board, index, column)
call MultiboardSetItemWidth(i, width)
call MultiboardReleaseItem(i)
set index = index + 1
endloop
set i = null
endmethod
method setText takes integer row, integer column, string str returns nothing
local multiboarditem i = MultiboardGetItem(board, row, column)
call MultiboardSetItemValue(i, str)
call MultiboardReleaseItem(i)
set i = null
endmethod
method setIcon takes integer row, integer column, string str returns nothing
local multiboarditem i = MultiboardGetItem(board, row, column)
call MultiboardSetItemIcon(i, str)
call MultiboardReleaseItem(i)
set i = null
endmethod
method setColor takes integer row, integer column, integer red, integer green, integer blue, integer alpha returns nothing
local multiboarditem i = MultiboardGetItem(board, row, column)
call MultiboardSetItemValueColor(i, red, green, blue, alpha)
call MultiboardReleaseItem(i)
set i = null
endmethod
method setStyle takes integer row, integer column, boolean showValue, boolean showIcon returns nothing
local multiboarditem i = MultiboardGetItem(board, row, column)
call MultiboardSetItemStyle(i, showValue, showIcon)
call MultiboardReleaseItem(i)
set i = null
endmethod
method setWidth takes integer row, integer column, real width returns nothing
local multiboarditem i = MultiboardGetItem(board, row, column)
call MultiboardSetItemWidth(i, width)
call MultiboardReleaseItem(i)
set i = null
endmethod
endstruct
endlibrary
//TESH.scrollpos=240
//TESH.alwaysfold=0
//! novjass ( disables the JassHelper until the next endnovjass declaration )
//=========================================================================
// Collection of the entire Inventory API
//=========================================================================
//=========================================================================
// Item classes.
// Every custom item requires an item class
// to determine into which slots the item can go.
// The root of every class is ItemClass.ANY ( 1 )
//=========================================================================
// Example:
set ITEM_CLASS_HAND = ItemClass.create("Hand")
set ITEM_CLASS_WEAPON = ITEM_CLASS_HAND.createSubClass("Weapon")
set ITEM_CLASS_SWORD = ITEM_CLASS_WEAPON.createSubClass("Sword")
// • An item of class "sword" can only go in a sword slot.
// • An item of class "hand" can go in an hand, weapon and sword slot.
// • An item of class "ANY" can go into any slow.
struct ItemClass extends array
// Fiels you can read.
readonly thistype parent
readonly string name
// Every item class is based of ANY.
static constant thistype ANY = 1
// Returns true for this == ANY
method operator root takes nothing returns boolean
// Every class is a sub class of ItemClass.ANY.
static method create takes string name returns thistype
// Any -> Hand -> Weapon -> Sword -> Mighty Sword ....
method createSubClass takes string name returns thistype
//=========================================================================
// Extended item object data.
// These function are ment to call on map init.
//=========================================================================
//=========================================================================
// Item registration.
//=========================================================================
// Registers an item id as custom item.
function RegisterCustomItemId takes integer itemId, ItemClass class returns nothing
// • Item ids of type ITEM_TYPE_POWERUP are invalid.
// • Item ids of type ITEM_TYPE_PURCHASABLE are valid, but remain in the native inventory interface.
function IsCustomItemId takes integer itemId returns boolean
function IsCustomItem takes item whichItem returns boolean
// Returns true if a given item is of ItemClass "class".
function IsCustomItemClass takes item whichItem, ItemClass class returns boolean
function GetCustomItemIdClass takes integer itemId returns ItemClass
function GetCustomItemClass takes item whichItem returns ItemClass
//===================================================================================
// Icon path and icon disabled path.
//===================================================================================
// If you don't set a DIS path, the default DIS path
// from the user setup library is accessed instead.
// Set paths for the visual objects.
// You can use string paths for images or raws for destructable objects or both.
function SetCustomItemIcon takes integer itemId, string filePath, string DISfilePath, integer destId, integer DISdestId returns nothing
// • Example 1: call SetCustomItemIcon('I000', null, null, 'B000', 'B001')
// • Example 2: call SetCustomItemIcon('I001', itemImageTextureFilePath.tga", itemImageTextureDisabledFilePath.tga, 0, 0)
function GetCustomItemIconPath takes integer itemId returns string
function GetCustomItemIconDISPath takes integer itemId returns string
function GetCustomItemIconId takes integer itemId returns integer
function GetCustomItemIconDISId takes integer itemId returns integer
//===================================================================================
// Twohanded items.
//===================================================================================
// This function declares an item id as a twohanded item type.
// By default every item id is not twohanded.
function SetCustomItemTwohand takes integer itemId, boolean flag returns nothing
function IsCustomItemTwohand takes item whichItem returns boolean
//===================================================================================
// Special effects. Animation properties. Complex special effects.
//===================================================================================
// These effects are added when items are equipped.
// AddSpecialEffectTarget(path, inventoryUnit, pos)
function SetCustomItemFxFilePath takes integer itemId, string path, string pos returns nothing
// For effects with multiple attach point names an ability is the best choice.
// The ability should serve as pure visual and don't add any further bonuses.
function SetCustomItemFxAbilityId takes integer itemId, integer abilityId returns nothing
// Animation tag of the item. By default it is "".
// AddUnitAnimationProperties(inventoryUnit, tag, true)
function SetCustomItemAnimationProperty takes integer itemId, string tag returns nothing
function GetCustomItemFxFilePath takes integer itemId returns string
function GetCustomItemFxPos takes integer itemId returns string
function GetCustomItemFxAbilityId takes integer itemId returns integer
function GetCustomItemAnimationProperty takes integer itemId returns string
// Complex fx.
function SetCustomItemFxAbilityId takes integer itemId, integer abilityId returns nothing
call SaveInteger(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA + 3, itemId, abilityId)
endfunction
function GetCustomItemFxAbilityId takes integer itemId returns integer
//===================================================================================
// Item socket count from min to max. Min == 0, Max == 6
//===================================================================================
// BOTH FUNCTIONS ARE NOT WORKING YET.
function SetCustomItemIdSockets takes integer itemId, integer min, integer max returns nothing
// Randomizes a socket count from min - max.
function GetCustomItemIdSockets takes integer itemId returns integer
//===================================================================================
// Item costs and charges.
//===================================================================================
function GetItemTypeIdGoldCost takes integer id returns integer
function GetItemTypeIdWoodCost takes integer id returns integer
function GetItemTypeIdCharges takes integer id returns integer
function GetItemGoldCost takes item i returns integer
function GetItemWoodCost takes item i returns integer
//===================================================================================
// Misc wrappers functions of CustomItem
//===================================================================================
// ITEM_TYPE_PURCHASABLE
function IsItemPurchasable takes item whichItem returns boolean
//===================================================================================
// Struct CustomItem
//===================================================================================
struct CustomItem extends array
// Is the item allocated or not.
method operator exists takes nothing returns boolean
// Equals GetHandleId(i). Always use CustomItem[]
static method operator [] takes item i returns thistype
// Returns the item handle of an handle id.
method getItem takes nothing returns item
method getItemId takes nothing returns integer
// Use getItem() instead.
method getHandle takes nothing returns item
// Set an owning unit.
method getOwner takes nothing returns unit
method setOwner takes unit source returns nothing
// Not implemented. ( Item cooldowns )
method isReady takes nothing returns boolean
// You may not call these two functions!
method identify takes nothing returns nothing
method isIdentified takes nothing returns boolean
// Returns true for a total item merging. MAX_CHARGES = 99.
method mergeCharges takes item add returns boolean
method getClass takes nothing returns ItemClass
// From 0.00 - 1.00 ( 0% - 100%)
method getQuality takes nothing returns real
method setQuality takes real value returns nothing
method getWoodCost takes nothing returns integer
method getGoldCost takes nothing returns integer
// Returns custom name if set. Otherwise GetItemName()
method getName takes nothing returns string
method setName takes string name returns nothing
method isItemPawnPossible takes unit seller, unit shop returns boolean
method pawnItem takes unit shop returns nothing
// ( Not implemented yet )
method isItemBuyPossible takes unit buyer, unit seller returns boolean
//=========================================================================
// Item affixes.
// These are lists of Bonuses or abilties
// an item should add to a unit when beeing equipped.
//=========================================================================
// Registers an abiltity to an item id.
function AddCustomItemIdAbility takes integer itemId, integer abilityId, integer level returns nothing
// Requires library BonusMod: Register Bonuses to item id.
function AddCustomItemIdBonus takes integer itemId, integer bonus, integer amount returns nothing
// Those functions register affixes directly to item handles.
// The item parser requires them for dynamic item data,
// you probably don't need these functions.
function AddCustomItemAbility takes item i, integer abilityId, integer level returns nothing
function AddCustomItemBonus takes item i, integer bonus, integer amount returns nothing
function FlushCustomItemAffixes takes item i returns nothing
//=========================================================================
// Dynamic items. ( Roll random affixes )
// These are item ids which can have random affixes.
//=========================================================================
// Dynamic items inherit static bonuses ( above ),
// therefore you can declare affixes an item id should
// always get and combine it with affixes it rolls dynamically.
// You can also declare constant affixes which roll in range min - max. ( See below at 6.) )
// 1.) Random affixes are rolled over loop indexes. (1, 2, 3, ..... )
// 2.) Loop indexes start off with 1. Looping stops at the highest defined index of that item id.
// 3.) Normally you only define the first loop index.
// 4.) Per loop index an affix can only be rolled once.
// 5.) Per loop index a random amount ( minimum - maximum ) affixes are rolled.
// 6.) Affixes added to loop index 0 are considered as constant and will roll always.
//
// Example: call SetAffixRangeForLoopIndex('I000', 1, 2, 5)
// call SetAffixRangeForLoopIndex('I000', 2, 1, 2)
// --> Rolls 2 - 5 affixes in loop index 1.
// --> Then 1 - 2 affixes in loop index 2.
function SetAffixRangeForLoopIndex takes integer itemId, integer index, integer minimum, integer maximum returns nothing
// Add affixes to a loop index.
// Example: AddItemAffix('I000', 0, "Bonus", BONUS_DAMAGE, 10, 20)
// AddItemAffix('I000', 1, "Bonus", BONUS_LIFE, 100, 200)
// AddItemAffix('I000', 1, "Bonus", BONUS_MANA, 10, 20)
// AddItemAffix('I000', 1, "Bonus", BONUS_Strength, 2, 2)
// --> For loop 1 the item can roll between 100 - 200 life bonus. Eventually it rolls the mana or strength bonus instead.
// --> Items of type id 'I000' will always roll between 10 and 20 damage.
// Valid "affixType" arguments are "Ability" or "Bonus". ( Case insensitiv - "BONUS" or "BoNUs" would also be ok )
function AddItemAffix takes integer itemId, integer loopIndex, string affixType, integer affixId, real min, real max returns nothing
//=========================================================================
// Inventory interface.
//=========================================================================
// Each inventory has a hardcoded size of 1500*833 in plane x / y.
function CreateInventory takes unit source, real originX, real originY returns Inventory
function DestroyInventory takes unit source returns nothing
// Returns the Inventory instance for a unit.
// Returns 0 for units without inventory.
function GetUnitInventory takes unit source returns Inventory
function ShowUnitInventory takes unit source, boolean flag returns nothing
function AddUnitInventoryDummy takes unit source, integer dummyId, real dummyY returns nothing
// • Optional function with adds a portrait to the interface.
// • "dummyY" is the offset in plane y inside the dummy window. ( Different per unit id )
//===================================================================================
// Inventory events.
// When an event trigger fires these values allow
// the action code to determine which event was dispatched.
// The functions listed below can be used to get information about the event.
//===================================================================================
globals
//=========================================
// Available Event Ids.
//=========================================
// RegisterInventoryEvent(eventId, code)
constant integer EVENT_INVENTORY_CREATED = 0
constant integer EVENT_INVENTORY_UNIT_EQUIP_ITEM = 1
constant integer EVENT_INVENTORY_UNIT_UNEQUIP_ITEM = 2
constant integer EVENT_INVENTORY_UNIT_PICKUP_ITEM = 3
constant integer EVENT_INVENTORY_UNIT_DROP_ITEM = 4
constant integer EVENT_INVENTORY_UNIT_PAWN_ITEM = 5
constant integer EVENT_INVENTORY_UNIT_BUY_ITEM = 6// ( Not working yet )
constant integer EVENT_INVENTORY_UNIT_TRADE_ITEM = 7// ( Not working yet )
endglobals
//=========================================
// Inventory Trigger Interface.
//=========================================
// Returns the trigger handle for the specified event id.
// Implemented for usage with the native trigger interface.
// Use this function very carefully.
function GetInventoryTrigger takes integer eventId returns trigger
//=======================================================
// Trigger Inventory Event API.
//=======================================================
// These functions only return their proper information when called
// from within a registered trigger action function.
// For incorrect usage they will either return "null", "-1" or "0".
// Returns the triggering Inventory instance.
constant function GetTriggerInventory takes nothing returns Inventory
// Returns the interacting item.
constant function Inventory_GetTriggerItem takes nothing returns item
// Returns the interacting unit.
// For most events this function is equal to Inventory_GetTriggerUnit().
constant function Inventory_GetEventTargetUnit takes nothing returns unit
// Returns the most recent event id.
constant function Inventory_GetTriggerEventId takes nothing returns integer
// Returns the inventory owning unit.
// Specified for events where Inventory_GetEventTargetUnit()
// doesn't return the inventory source unit. ( Shop events )
constant function Inventory_GetTriggerUnit takes nothing returns unit
// Registers code to available inventory events.
// Condition functions for inventory events don't have to return a boolean.
function RegisterInventoryEvent takes integer eventId, code func returns nothing
//=======================================================
// Struct Inventory.
//=======================================================
struct Inventory extends UIScreen
readonly unit source// Owning unit.
readonly unit dummy
readonly real dummyY
readonly unit shop
// AddUnitAnimationProperties()
readonly string animation
// The global user setup defines a default font for all instances,
// you can change it at any time via this.setFont(font).
private integer fontType
method getFont takes nothing returns integer
method setFont takes integer newFont returns nothing
// The global user setup defines a default color for all instances,
// you can change it any time via this.setColor(ARGB)
private integer color
method getColor takes nothing returns ARGB
method setColor takes integer newColor returns nothing
method unitHasItemEquipped takes item whichItem returns boolean
method unitHasItemIdEquipped takes integer itemId returns boolean
method mergeItems takes item target, item source returns boolean
method unitRemoveItem takes item whichItem returns boolean
method unitUnequipItemToSlot takes item whichItem, InventoryCell slot returns boolean
// Equips items to the unit.
method unitEquipItemToSlot takes item whichItem, InventoryCell slot, boolean forceAction returns boolean
method unitEquipItem takes item whichItem returns boolean
// Only accepts Backpack cells as slot.
method unitAddItemToSlot takes item whichItem, InventoryCell slot returns boolean
method unitAddItem takes item whichItem returns boolean
// UNDER CONSTRUCTION.
method unitPawnItem takes item whichItem, unit shop returns boolean
// UNDER CONSTRUCTION.
method unitBuyItem takes item whichItem, unit shop returns boolean
// UNDER CONSTRUCTION.
method unitTradeItem takes item whichItem, thistype partner returns boolean
// UNDER CONSTRUCTION.
method socketItem takes item whichItem, item gem returns boolean
// UNDER CONSTRUCTION.
method unsocketItem takes item whichItem returns boolean
//=======================================================
// Struct id getters.
//=======================================================
constant function GetInventoryEquipmentTypeId takes nothing returns integer
constant function GetInventoryBackpackTypeId takes nothing returns integer
constant function GetInventoryMerchantTypeId takes nothing returns integer
constant function GetInventoryNativeInventoryTypeId takes nothing returns integer
constant function GetInventoryDummyTypeId takes nothing returns integer
constant function GetInventoryControllTypeId takes nothing returns integer
constant function GetInventoryTradeTypeId takes nothing returns integer
//! endnovjass
//TESH.scrollpos=105
//TESH.alwaysfold=0
library InventoryUserSetup
//=========================================================================
// Collection of all fields
// you can customize in Inventory.
//=========================================================================
//===========================================================================
// External object merger. ( LUA )
// This tool can generate a bunch of chosen "Object Editor" objects for you,
// so you don't have to copy 'n' paste everything from this map to your map.
//
// Follow the instructions below
// to run the LUA script.
//===========================================================================
globals
constant boolean INVENTORY_GENERATE_OBJECTS = false
// 1.) Enable the "Inventory Generate Objects" trigger.
// 2.) Set INVENTORY_GENERATE_OBJECTS = true.
// 3.) Save your map, close the editor, start the editor and open your map.
// 4.) Set INVENTORY_GENERATE_OBJECTS = false.
// 5.) Disable or delete the "Inventory Generate Objects" trigger.
endglobals
//=========================================
// Inventory constants.
// Like the game play constants,
// but for Inventory.
//=========================================
globals
// Item related.
constant boolean INVENTORY_ENABLE_DYNAMIC_ITEMS = true
// Shop related.
constant real INVENTORY_SELL_ITEM_RETURN_RATE = 0.5
constant integer INVENTORY_MAXIMUM_SHOP_RANGE = 450
constant integer INVENTORY_MERCHANT_EXTRA_PAGES = 0 // ( Not working yet )
// Tooltip related.
constant string INVENTORY_RESOURCE_GOLD_ICON = "UI\\Feedback\\Resources\\ResourceGold.blp"
constant string INVENTORY_RESOURCE_WOOD_ICON = "UI\\Feedback\\Resources\\ResourceLumber.blp"
// Font related.
constant real INVENTORY_FONT_SIZE = 8// Recommended setting is 8.
// Cell selector.
constant integer INVENTORY_CELL_SELECTOR_ID = 'Bisa'
constant real INVENTORY_CELL_SELECTOR_SCALE = .75// Relative to a base of 64x64.
// Default icon and DIS icon.
// This file / id is accessed, when no path / id is set for an object.
// The string refers to an image file path, the id to a destructable object.
// If declared the string file path is used over the the destructable id.
constant string INVENTORY_ITEM_ICON_PATH = null
constant integer INVENTORY_ITEM_ICON_ID = 'Biba'
constant string INVENTORY_ITEM_DIS_ICON_PATH = null
constant integer INVENTORY_ITEM_DIS_ICON_ID = 'Biba'
endglobals
//=============================================
// Global default values.
// These informactions are used,
// if not specified different per instance.
//=============================================
// Default font for textsplats in every Inventory instance.
// Use instance.setFont(desiredFont) to change the font of an inventory instances.
constant function Inventory_GetDefaultFont takes nothing returns font
return TREBUCHET_MS
endfunction
// Set the default font color for interface errors in ARGB.
// instance.setColor(ARGB) allows you to change the font color of an inventory instance.
function Inventory_GetDefaultFontColor takes nothing returns ARGB
return ARGB.create(255, 218, 165, 32)
endfunction
//==================================================
// Struct and race based fields.
// The setup below is specific for each
// individual interface window of Inventory.
//==================================================
//=========================================
// Equipment related setup.
//=========================================
// To change the visual layout and / or the item classes of buttons, go to library "Inventory Equipment",
// --> "static method onCreate takes Inventory inventory returns thistype"
// and configurate the buttons coordinates and data to your needs.
// Set user interface border fields.
function Inventory_SetEquipmentBorderFields takes unit source returns nothing
local race r = GetUnitRace(source)
// Hint: UIBorder uses a default setting ( null ) for each border brick, which is used if
// you don't specify the path. You can also change the default setting inside the UIBasic library.
if (r == RACE_ORC) then
set UI_BORDER_LEFT = "war3mapImported\\BorderLeftOC.TGA"
set UI_BORDER_RIGHT = "war3mapImported\\BorderRightOC.TGA"
set UI_BORDER_TOP = "war3mapImported\\BorderUpOC.TGA"
set UI_BORDER_BOTTOM = "war3mapImported\\BorderDownOC.TGA"
set UI_BORDER_BOTTOM_LEFT = "war3mapImported\\BorderDownLeftOC.TGA"
set UI_BORDER_BOTTOM_RIGHT = "war3mapImported\\BorderDownRightOC.TGA"
set UI_BORDER_TOP_LEFT = "war3mapImported\\BorderUpLeftOC.TGA"
set UI_BORDER_TOP_RIGHT = "war3mapImported\\BorderUpRightOC.TGA"
elseif (r == RACE_NIGHTELF) then
set UI_BORDER_LEFT = "war3mapImported\\BorderLeftNE.TGA"
set UI_BORDER_RIGHT = "war3mapImported\\BorderRightNE.TGA"
set UI_BORDER_TOP = "war3mapImported\\BorderUpNE.TGA"
set UI_BORDER_BOTTOM = "war3mapImported\\BorderDownNE.TGA"
set UI_BORDER_BOTTOM_LEFT = "war3mapImported\\BorderDownLeftNE.TGA"
set UI_BORDER_BOTTOM_RIGHT = "war3mapImported\\BorderDownRightNE.TGA"
set UI_BORDER_TOP_LEFT = "war3mapImported\\BorderUpLeftNE.TGA"
set UI_BORDER_TOP_RIGHT = "war3mapImported\\BorderUpRightNE.TGA"
endif
set UI_BORDER_BACKGROUND_COLOR = ARGB.create(255, 0, 0, 0)
set UI_BORDER_BACKGROUND = "war3mapImported\\background.TGA"
set r = null
endfunction
//=========================================
// Backpack related setup.
//=========================================
// To change the visual layout and / or the item classes of buttons, go to library "Inventory Backpack",
// --> "static method onCreate takes Inventory inventory returns thistype"
// and configurate the buttons coordinates and data to your needs.
// Set the backpack row count.
constant function Inventory_GetBackpackRowCount takes unit source returns integer
return 4
endfunction
// Set the backpack column count.
constant function Inventory_GetBackpackColumnCount takes unit source returns integer
return 5
endfunction
// Add extra pages to the backpack. If you don't want to add pages pass in 0 or 1.
constant function Inventory_GetBackpackPageCount takes unit source returns integer
return 3
endfunction
// Set the path for the image used as empty slot.
constant function Inventory_GetBackpackSlotFilePath takes unit source returns string
return "UI\\Console\\Human\\human-transport-slot.blp"
endfunction
// Set user interface border fields. For full API check the available fields for UIBorder.
function Inventory_SetBackpackBorderFields takes unit source returns nothing
local race r = GetUnitRace(source)
// Hint: UIBorder uses a default setting ( null ) for each border brick, which is used if
// you don't specify the path. You can change the default setting inside the UIBasic library.
if (r == RACE_ORC) then
set UI_BORDER_LEFT = "war3mapImported\\BorderLeftOC.TGA"
set UI_BORDER_RIGHT = "war3mapImported\\BorderRightOC.TGA"
set UI_BORDER_TOP = "war3mapImported\\BorderUpOC.TGA"
set UI_BORDER_BOTTOM = "war3mapImported\\BorderDownOC.TGA"
set UI_BORDER_BOTTOM_LEFT = "war3mapImported\\BorderDownLeftOC.TGA"
set UI_BORDER_BOTTOM_RIGHT = "war3mapImported\\BorderDownRightOC.TGA"
set UI_BORDER_TOP_LEFT = "war3mapImported\\BorderUpLeftOC.TGA"
set UI_BORDER_TOP_RIGHT = "war3mapImported\\BorderUpRightOC.TGA"
elseif (r == RACE_NIGHTELF) then
set UI_BORDER_LEFT = "war3mapImported\\BorderLeftNE.TGA"
set UI_BORDER_RIGHT = "war3mapImported\\BorderRightNE.TGA"
set UI_BORDER_TOP = "war3mapImported\\BorderUpNE.TGA"
set UI_BORDER_BOTTOM = "war3mapImported\\BorderDownNE.TGA"
set UI_BORDER_BOTTOM_LEFT = "war3mapImported\\BorderDownLeftNE.TGA"
set UI_BORDER_BOTTOM_RIGHT = "war3mapImported\\BorderDownRightNE.TGA"
set UI_BORDER_TOP_LEFT = "war3mapImported\\BorderUpLeftNE.TGA"
set UI_BORDER_TOP_RIGHT = "war3mapImported\\BorderUpRightNE.TGA"
endif
set UI_BORDER_BACKGROUND = null
endfunction
//=========================================
// Controll panel related setup.
//=========================================
// To change the visual layout, go to library "Inventory Controll",
// --> "static method onCreate takes Inventory inventory returns thistype"
// and configurate the buttons coordinates to your needs.
// Set the path for the scroll up arrow.
constant function Inventory_GetScrollUpPath takes unit source returns string
return "war3mapImported\\ScrollUp.tga"
endfunction
// Set the path for the scroll down arrow.
constant function Inventory_GetScrollDownPath takes unit source returns string
return "war3mapImported\\ScrollDown.tga"
endfunction
// Set the destructable id for the drop item button. You can also use an image file,
// but then you have to change addDestButton to addImageButton inside the library.
constant function Inventory_GetDropButtonId takes unit source returns integer
return 'Bibb'
endfunction
// Set the destructable id for the close inventory button. You can also use an image file,
// but then you have to change addDestButton to addImageButton inside the library.
constant function Inventory_GetCloseButtonId takes unit source returns integer
return 'Biba'
endfunction
// Set the destructable id for the pawn item button.
constant function Inventory_GetPawnButtonId takes unit source returns integer
return 'Bibc'
endfunction
// Set the destructable id for the buy item button.
constant function Inventory_GetBuyButtonId takes unit source returns integer
return 'Bibd'
endfunction
// Set the background image path.
constant function Inventory_GetBackgroundFilePath takes unit source returns string
return "UI\\Widgets\\EscMenu\\Human\\human-options-button-background-disabled.blp"
endfunction
// Colorize the background image via an ARGB value.
function Inventory_GetBackgroundColor takes unit source returns ARGB
if (GetUnitRace(source) == RACE_NIGHTELF) then
return ARGB.create(255, 150, 255, 150)
endif
return ARGB.create(255, 139, 69, 19)
endfunction
//===========================================
// Dummy window and affix box related setup.
//===========================================
// This setup relates to the unit affix box,
// which is displayed when clicking the user interface dummy unit.
//
// The affix box is one the more heavy operations within the inventory source code.
// Please do not overdo it in terms of text rows. Another factor are color codes.
// Color codes have to be translated to hexadecimal, then to ARGB and through this add overhead.
//
// The box automatically updates on equip and unequip.
// You can update it manually by calling:
//
// function Inventory_UpdateAffixBox takes unit source returns nothing
// Set which units should allocate an affix box at all.
function Inventory_AllocateAffixBoxForUnit takes unit source returns boolean
return IsUnitType(source, UNIT_TYPE_HERO)
endfunction
// Set the headline text.
function Inventory_GetAffixBoxHeadline takes unit source returns string
return "|cffdaa520Attributes:|r"
endfunction
// Set the amount of rows inside the box.
function Inventory_GetAffixBoxRowCount takes unit source returns integer
return 4
endfunction
// Set the string per row within the affix box. First row index is 0.
function Inventory_GetAffixBoxRowString takes unit source, integer row returns string
if (row == 0) then
return"|cffdaa520Strength:|r " + I2S(GetHeroStr(source, true))
elseif (row == 1) then
return "|cffdaa520Agility:|r " + I2S(GetHeroAgi(source, true))
elseif (row == 2) then
return "|cffdaa520Intelligence:|r " + I2S(GetHeroInt(source, true))
endif
return "|cffdaa520Movement Speed:|r " + I2S(R2I(GetUnitMoveSpeed(source)))
endfunction
// Set border fields for the dummy window.
function Inventory_SetDummyBorderFields takes unit source returns nothing
local race r = GetUnitRace(source)
if r == RACE_NIGHTELF then
set UI_BORDER_BACKGROUND = "UI\\Glues\\Loading\\Backgrounds\\Campaigns\\NightElfSymbol.blp"
set UI_BORDER_LEFT = "war3mapImported\\BorderLeftNE.TGA"
set UI_BORDER_RIGHT = "war3mapImported\\BorderRightNE.TGA"
set UI_BORDER_TOP = "war3mapImported\\BorderUpNE.TGA"
set UI_BORDER_BOTTOM = "war3mapImported\\BorderDownNE.TGA"
set UI_BORDER_BOTTOM_LEFT = "war3mapImported\\BorderDownLeftNE.TGA"
set UI_BORDER_BOTTOM_RIGHT = "war3mapImported\\BorderDownRightNE.TGA"
set UI_BORDER_TOP_LEFT = "war3mapImported\\BorderUpLeftNE.TGA"
set UI_BORDER_TOP_RIGHT = "war3mapImported\\BorderUpRightNE.TGA"
elseif r == RACE_ORC then
set UI_BORDER_BACKGROUND = "UI\\Glues\\Loading\\Backgrounds\\Campaigns\\OrcSymbol.blp"
set UI_BORDER_LEFT = "war3mapImported\\BorderLeftOC.TGA"
set UI_BORDER_RIGHT = "war3mapImported\\BorderRightOC.TGA"
set UI_BORDER_TOP = "war3mapImported\\BorderUpOC.TGA"
set UI_BORDER_BOTTOM = "war3mapImported\\BorderDownOC.TGA"
set UI_BORDER_BOTTOM_LEFT = "war3mapImported\\BorderDownLeftOC.TGA"
set UI_BORDER_BOTTOM_RIGHT = "war3mapImported\\BorderDownRightOC.TGA"
set UI_BORDER_TOP_LEFT = "war3mapImported\\BorderUpLeftOC.TGA"
set UI_BORDER_TOP_RIGHT = "war3mapImported\\BorderUpRightOC.TGA"
endif
// Higher layer than the equipment.
set UI_BORDER_BACKGROUND_IMAGE_TYPE = IMAGE_TYPE_OCCLUSION_MASK
set r = null
endfunction
// Set border fields for the affix box window.
function Inventory_SetAffixBoxBorderFields takes unit source returns nothing
local race r = GetUnitRace(source)
// Hint: UIBorder uses a default setting ( null ) for each border brick, which is used if
// you don't specify the path. You can change the default setting inside the UIBasic library.
if (r == RACE_ORC) then
set UI_BORDER_LEFT = "war3mapImported\\BorderLeftOC.TGA"
set UI_BORDER_RIGHT = "war3mapImported\\BorderRightOC.TGA"
set UI_BORDER_TOP = "war3mapImported\\BorderUpOC.TGA"
set UI_BORDER_BOTTOM = "war3mapImported\\BorderDownOC.TGA"
set UI_BORDER_BOTTOM_LEFT = "war3mapImported\\BorderDownLeftOC.TGA"
set UI_BORDER_BOTTOM_RIGHT = "war3mapImported\\BorderDownRightOC.TGA"
set UI_BORDER_TOP_LEFT = "war3mapImported\\BorderUpLeftOC.TGA"
set UI_BORDER_TOP_RIGHT = "war3mapImported\\BorderUpRightOC.TGA"
elseif (r == RACE_NIGHTELF) then
set UI_BORDER_LEFT = "war3mapImported\\BorderLeftNE.TGA"
set UI_BORDER_RIGHT = "war3mapImported\\BorderRightNE.TGA"
set UI_BORDER_TOP = "war3mapImported\\BorderUpNE.TGA"
set UI_BORDER_BOTTOM = "war3mapImported\\BorderDownNE.TGA"
set UI_BORDER_BOTTOM_LEFT = "war3mapImported\\BorderDownLeftNE.TGA"
set UI_BORDER_BOTTOM_RIGHT = "war3mapImported\\BorderDownRightNE.TGA"
set UI_BORDER_TOP_LEFT = "war3mapImported\\BorderUpLeftNE.TGA"
set UI_BORDER_TOP_RIGHT = "war3mapImported\\BorderUpRightNE.TGA"
endif
set UI_BORDER_BACKGROUND_COLOR = ARGB.create(255, 0, 0, 0)
set UI_BORDER_BACKGROUND = "war3mapImported\\background.TGA"
endfunction
//===========================================
// Native Inventory related setup.
//===========================================
constant function Inventory_GetSlotFiller takes unit source returns string
return "war3mapImported\\background.TGA"
endfunction
//===========================================
// Tooltip box related setup.
//===========================================
function Inventory_SetTooltipBoxBorderFields takes unit source returns nothing
local race r = GetUnitRace(source)
// Hint: UIBorder uses a default setting ( null ) for each border brick, which is used if
// you don't specify the path. You can change the default setting inside the UIBasic library.
if (r == RACE_ORC) then
set UI_BORDER_LEFT = "war3mapImported\\BorderLeftOC.TGA"
set UI_BORDER_RIGHT = "war3mapImported\\BorderRightOC.TGA"
set UI_BORDER_TOP = "war3mapImported\\BorderUpOC.TGA"
set UI_BORDER_BOTTOM = "war3mapImported\\BorderDownOC.TGA"
set UI_BORDER_BOTTOM_LEFT = "war3mapImported\\BorderDownLeftOC.TGA"
set UI_BORDER_BOTTOM_RIGHT = "war3mapImported\\BorderDownRightOC.TGA"
set UI_BORDER_TOP_LEFT = "war3mapImported\\BorderUpLeftOC.TGA"
set UI_BORDER_TOP_RIGHT = "war3mapImported\\BorderUpRightOC.TGA"
elseif (r == RACE_NIGHTELF) then
set UI_BORDER_LEFT = "war3mapImported\\BorderLeftNE.TGA"
set UI_BORDER_RIGHT = "war3mapImported\\BorderRightNE.TGA"
set UI_BORDER_TOP = "war3mapImported\\BorderUpNE.TGA"
set UI_BORDER_BOTTOM = "war3mapImported\\BorderDownNE.TGA"
set UI_BORDER_BOTTOM_LEFT = "war3mapImported\\BorderDownLeftNE.TGA"
set UI_BORDER_BOTTOM_RIGHT = "war3mapImported\\BorderDownRightNE.TGA"
set UI_BORDER_TOP_LEFT = "war3mapImported\\BorderUpLeftNE.TGA"
set UI_BORDER_TOP_RIGHT = "war3mapImported\\BorderUpRightNE.TGA"
endif
set UI_BORDER_BACKGROUND = "war3mapImported\\background.TGA"
endfunction
//===================================================================================
// This block serves to configurate the
// item parsing process to your needs without
// messing with the source code.
//===================================================================================
//! textmacro ITEM_PARSER_ITEM_TOOLTIP_SUFFIXES takes DO
static if $DO$ then
//===================================================================================
// Bonus tooltip suffixes.
//===================================================================================
// Example: +15 Armor.
// The "+" string is harcoded into the parser.
// The value "15" is internally read out from the bonus list.
// The suffix "Armor" can be configurated in this macro.
static if LIBRARY_BonusMod then
call SetSuffix(BONUS_AGILITY, "|cff0066ccAgility|r")
call SetSuffix(BONUS_STRENGTH, "|cff0066ccStrength|r")
call SetSuffix(BONUS_INTELLIGENCE, "|cff0066ccIntelligence|r")
call SetSuffix(BONUS_ARMOR, "|cff0066ccArmor|r")
call SetSuffix(BONUS_DAMAGE, "|cff0066ccDamage|r")
call SetSuffix(BONUS_ATTACK_SPEED, "|cff0066ccAttack Speed|r")
call SetSuffix(BONUS_SIGHT_RANGE, "|cff0066ccSight Range|r")
call SetSuffix(BONUS_ATTACK_SPEED, "|cff0066ccAttack Speed|r")
call SetSuffix(BONUS_LIFE_REGEN, "|cff0066ccLife Regeneration|r")
call SetSuffix(BONUS_MANA_REGEN_PERCENT, "|cff99b4d1%|r |cff0066ccMana Regeneration|r")
endif
static if LIBRARY_RegenBonuses and LIBRARY_BonusMod then
call SetSuffix(BONUS_MANA_REGEN, "|cff0066ccMana Regeneration|r")
call SetSuffix(BONUS_LIFE_REGEN_PERCENT, "|cff99b4d1%|r |cff0066ccLife Regeneration|r")
endif
static if LIBRARY_UnitMaxStateBonuses and LIBRARY_BonusMod then
call SetSuffix(BONUS_LIFE, "|cff0066ccLife|r")
call SetSuffix(BONUS_MANA, "|cff0066ccMana|r")
endif
static if LIBRARY_MovementBonus and LIBRARY_BonusMod then
call SetSuffix(BONUS_MOVEMENT_SPEED, "|cff0066ccMovement Speed|r")
endif
endif
//! endtextmacro
//! textmacro ITEM_PARSER_RANDOM_ITEM_NAME
//===================================================================================
// Inventory is able to generate
// random names for custom items, if
// the following conditions match:
// 1.) The item must be of type ITEM_TYPE_PERMANENT.
// 2.) The item must roll random affixes.
//===================================================================================
// Factors which influence the naming are:
// 1.) Item quality. ( In range of 0% - 100% )
// 2.) Strongest rolled affix.
//===================================================================================
// An item name is composed of
// prefix + GetItemName(item) + suffix.
// You can define all suffixes and prefixes in
// the Inventory User Setup.
//
// In case the strongest rolled affix is
// an ability id, the item name will become:
// GetPrefix(abilityId) + GetItemName(item) + of + GetObjectName(abilityId)
// However abilities always lookup their prefix on 0% quality.
//===================================================================================
// Initalize your affix suffixes and prefixes here.
// To speed up the process you can define them in range.
//
// For example SetPrefix("Strong", BONUS_DAMAGE, 0, 40)
// initializes the prefix "Strong" for BONUS_DAMAGE affixes
// in quality range from 0% to 40%.
//
// Minimum allowed quality: 0
// Maximum allowed quality: 100
static if LIBRARY_BonusMod then
// takes: text, bonus, quality min %, quality max %.
call SetPrefix("Jagged", BONUS_DAMAGE, 0, 20)
call SetPrefix("Deadly", BONUS_DAMAGE, 21, 40)
call SetPrefix("Brutal", BONUS_DAMAGE, 41, 60)
call SetPrefix("Cruel", BONUS_DAMAGE, 61, 80)
call SetPrefix("Merciless", BONUS_DAMAGE, 81, 100)
call SetSuffix("of Maiming", BONUS_DAMAGE, 0, 20)
call SetSuffix("of Strife", BONUS_DAMAGE, 21, 40)
call SetSuffix("of Doom", BONUS_DAMAGE, 41, 60)
call SetSuffix("of Agony", BONUS_DAMAGE, 61, 80)
call SetSuffix("of Death", BONUS_DAMAGE, 81, 100)
//
call SetPrefix("Dueling", BONUS_STRENGTH, 0, 20)
call SetPrefix("Cruel", BONUS_STRENGTH, 21, 40)
call SetPrefix("Dauntless", BONUS_STRENGTH, 41, 60)
call SetPrefix("Severe", BONUS_STRENGTH, 61, 80)
call SetPrefix("Murderous", BONUS_STRENGTH, 81, 100)
call SetSuffix("of the Lion", BONUS_STRENGTH, 0, 20)
call SetSuffix("of Invasion", BONUS_STRENGTH, 21, 40)
call SetSuffix("of Assault", BONUS_STRENGTH, 41, 60)
call SetSuffix("of Assault", BONUS_STRENGTH, 61, 80)
call SetSuffix("of Assault", BONUS_STRENGTH, 81, 100)
//
call SetPrefix("Feral", BONUS_AGILITY, 0, 20)
call SetPrefix("True", BONUS_AGILITY, 21, 40)
call SetPrefix("Worthy", BONUS_AGILITY, 41, 60)
call SetPrefix("Marvelous", BONUS_AGILITY, 61, 80)
call SetPrefix("Valiant", BONUS_AGILITY, 81, 100)
call SetSuffix("of the Hawk", BONUS_AGILITY, 0, 20)
call SetSuffix("of Cruelty", BONUS_AGILITY, 21, 40)
call SetSuffix("of Pain", BONUS_AGILITY, 41, 60)
call SetSuffix("of Pairon", BONUS_AGILITY, 61, 80)
call SetSuffix("of Pain", BONUS_AGILITY, 81, 100)
//
call SetPrefix("Clever", BONUS_INTELLIGENCE, 0, 20)
call SetPrefix("Magic", BONUS_INTELLIGENCE, 21, 40)
call SetPrefix("Champion", BONUS_INTELLIGENCE, 41, 60)
call SetPrefix("Parvagon", BONUS_INTELLIGENCE, 61, 80)
call SetPrefix("Magnus's", BONUS_INTELLIGENCE, 81, 100)
call SetSuffix("of Focus", BONUS_INTELLIGENCE, 0, 20)
call SetSuffix("of the Mind", BONUS_INTELLIGENCE, 21, 40)
call SetSuffix("of Omens", BONUS_INTELLIGENCE, 41, 60)
call SetSuffix("of Far Sight", BONUS_INTELLIGENCE, 61, 80)
call SetSuffix("of Far Sight", BONUS_INTELLIGENCE, 81, 100)
//
call SetPrefix("Worn-out", BONUS_ARMOR, 0, 20)
call SetPrefix("Wicked", BONUS_ARMOR, 21, 40)
call SetPrefix("Deadly", BONUS_ARMOR, 41, 60)
call SetPrefix("Perfect", BONUS_ARMOR, 61, 80)
call SetPrefix("Divine", BONUS_ARMOR, 81, 100)
call SetSuffix("of Strife", BONUS_ARMOR, 0, 20)
call SetSuffix("of Strife", BONUS_ARMOR, 21, 40)
call SetSuffix("of Strife", BONUS_ARMOR, 41, 60)
call SetSuffix("of Strife", BONUS_ARMOR, 61, 80)
call SetSuffix("of Strife", BONUS_ARMOR, 81, 100)
//
call SetPrefix("Brave", BONUS_ATTACK_SPEED, 0, 20)
call SetPrefix("Wicked", BONUS_ATTACK_SPEED, 21, 40)
call SetPrefix("Deadly", BONUS_ATTACK_SPEED, 41, 60)
call SetPrefix("Perfect", BONUS_ATTACK_SPEED, 61, 80)
call SetPrefix("Divine", BONUS_ATTACK_SPEED, 81, 100)
call SetSuffix("of the eagle", BONUS_ATTACK_SPEED, 0, 20)
call SetSuffix("of Focus", BONUS_ATTACK_SPEED, 21, 40)
call SetSuffix("of Speed", BONUS_ATTACK_SPEED, 41, 60)
call SetSuffix("of Speed", BONUS_ATTACK_SPEED, 61, 80)
call SetSuffix("of Speed", BONUS_ATTACK_SPEED, 81, 100)
//
static if LIBRARY_MovementBonus then
call SetPrefix("Brave", BONUS_MOVEMENT_SPEED, 0, 20)
call SetPrefix("Wicked", BONUS_MOVEMENT_SPEED, 21, 40)
call SetPrefix("Deadly", BONUS_MOVEMENT_SPEED, 41, 60)
call SetPrefix("Perfect", BONUS_MOVEMENT_SPEED, 61, 80)
call SetPrefix("Divine", BONUS_MOVEMENT_SPEED, 81, 100)
call SetSuffix("of the eagle", BONUS_MOVEMENT_SPEED, 0, 20)
call SetSuffix("of Focus", BONUS_MOVEMENT_SPEED, 21, 40)
call SetSuffix("of Speed", BONUS_MOVEMENT_SPEED, 41, 60)
call SetSuffix("of Speed", BONUS_MOVEMENT_SPEED, 61, 80)
call SetSuffix("of Speed", BONUS_MOVEMENT_SPEED, 81, 100)
endif
//
static if LIBRARY_UnitMaxStateBonuses then
call SetPrefix("Brave", BONUS_LIFE, 0, 20)
call SetPrefix("Wicked", BONUS_LIFE, 21, 40)
call SetPrefix("Deadly", BONUS_LIFE, 41, 60)
call SetPrefix("Perfect", BONUS_LIFE, 61, 80)
call SetPrefix("Divine", BONUS_LIFE, 81, 100)
call SetSuffix("of the eagle", BONUS_LIFE, 0, 20)
call SetSuffix("of Focus", BONUS_LIFE, 21, 40)
call SetSuffix("of Speed", BONUS_LIFE, 41, 60)
call SetSuffix("of Speed", BONUS_LIFE, 61, 80)
call SetSuffix("of Speed", BONUS_LIFE, 81, 100)
//
call SetPrefix("Brave", BONUS_MANA, 0, 20)
call SetPrefix("Wicked", BONUS_MANA, 21, 40)
call SetPrefix("Deadly", BONUS_MANA, 41, 60)
call SetPrefix("Perfect", BONUS_MANA, 61, 80)
call SetPrefix("Divine", BONUS_MANA, 81, 100)
call SetSuffix("of the eagle", BONUS_MANA, 0, 20)
call SetSuffix("of Focus", BONUS_MANA, 21, 40)
call SetSuffix("of Speed", BONUS_MANA, 41, 60)
call SetSuffix("of Speed", BONUS_MANA, 61, 80)
call SetSuffix("of Speed", BONUS_MANA, 81, 100)
endif
endif
//! endtextmacro
endlibrary
//TESH.scrollpos=69
//TESH.alwaysfold=0
library InventoryCore /*
//===================================================================================================
// Inventory Core.
// This library contains a collection of variables, constants and functions
// used in all libraries associated with the inventory interface.
// It also lists the required resources for Inventory.
//
// Don't change anything in here.
//===================================================================================================
*/ uses /*
*/ UIPackage /* - Available in this map.
*/ InventoryUserSetup /*
*/ Font /* hiveworkshop.com/forums/submissions-414/textsplat2-273717/
*/ Ascii /* hiveworkshop.com/forums/jass-functions-413/snippet-ascii-190746/
*/ WordWrap /* wc3jass.com/5016/system-wordwrap/
*/ TextSplat2 /* hiveworkshop.com/forums/submissions-414/textsplat2-273717/
*/ StringSize /* http://wc3jass.com/5014/snippet-stringsize/msg38482/topicseen/#new
*/ ErrorMessage /* github.com/nestharus/JASS/tree/master/jass/Systems/ErrorMessage
*/ RegisterPlayerUnitEvent /* hiveworkshop.com/forums/jass-resources-412/snippet-registerplayerunitevent-203338/
*/ optional BonusMod /* wc3c.net/showthread.php?t=107940
*/ optional GetItemCost /* github.com/nestharus/JASS/tree/master/jass/Systems/Costs%20-%20Unit%20%26%20Item
=====================================================================================================*/
// Credits to:
// Bribe: Ascii, Table
// Nestharus: Ascii, ErrorMessage
// PurgeandFire: StringSize, WordWrap
// Earth-Fury: BonusMod
// Magtheridon96: RegisterPlayerUnitEvent
//===================================================================================================
//===================================================================================
// Databank.
//===================================================================================
globals
private constant hashtable HASH = InitHashtable()
endglobals
// Access the databank.
constant function Inventory_GetTable takes nothing returns hashtable
return HASH
endfunction
//===================================================================================
// Parent Keys. Changing their values can break the entire Inventory system.
//===================================================================================
globals
constant integer INVENTORY_ITEM_COSTS = 0
constant integer INVENTORY_ITEM_CHARGES = 1
constant integer INVENTORY_ITEM_WOOD_COST = 2
constant integer INVENTORY_ITEM_GOLD_COST = 3
constant integer INVENTORY_KEY_CUSTOM_ITEM_NEXT = 4
constant integer INVENTORY_KEY_CUSTOM_ITEM_PREV = 5
constant integer INVENTORY_KEY_CUSTOM_ITEM = 6
constant integer INVENTORY_KEY_CUSTOM_ITEM_OWNER = 7
constant integer INVENTORY_KEY_CUSTOM_ITEM_CELL = 8
constant integer INVENTORY_KEY_ITEM_DATA = 9
constant integer INVENTORY_ITEM_NAME = 20
constant integer INVENTORY_KEY_ITEM_PARSER = 21
constant integer INVENTORY_KEY_ITEM_PARSER_SUFFIX = 22
constant integer INVENTORY_KEY_UNIT_REFERENCE = 23
//
constant integer INVENTORY_ITEM_TYPE_ID_AFFIXES = 24
constant integer INVENTORY_ITEM_TYPE_ID_ABILITY_LIST = 25
constant integer INVENTORY_ITEM_TYPE_ID_BONUS_LIST = 26
constant integer INVENTORY_ITEM_ID_AFFIXES = 27
constant integer INVENTORY_ITEM_ID_ABILITY_LIST = 28
constant integer INVENTORY_ITEM_ID_BONUS_LIST = 29
//
constant integer INVENTORY_LIST_LIST = 40
constant integer INVENTORY_LIST_NEXT = 41
constant integer INVENTORY_LIST_PREV = 42
constant integer INVENTORY_LIST_FIRST = 43
constant integer INVENTORY_LIST_LAST = 44
debug constant integer INVENTORY_LIST_NODES = 45
debug constant integer INVENTORY_LIST_LISTS = 46
//
constant integer INVENTORY_AFFIX_STACK = 50
constant integer INVENTORY_AFFIX_DEPTH_LIST = 51
constant integer INVENTORY_AFFIX_DEPTH_DEPTH = 52
constant integer INVENTORY_AFFIX_DEPTH_RANDOM = 53
constant integer INVENTORY_CONSTANT_AFFIX_LIST = 54
constant integer INVENTORY_CONSTANT_AFFIX_TYPES = 55
constant integer INVENTORY_RANDOM_AFFIX_LIST = 56
constant integer INVENTORY_RANDOM_AFFIX_TYPES = 57
endglobals
// Flush a child key.
function Inventory_FlushChild takes integer parent returns nothing
call FlushChildHashtable(HASH, parent)
endfunction
//===================================================================================
// Global linked list.
//===================================================================================
globals
private integer lists = 0
private integer nodes = 0
endglobals
// It would be naive to believe everything works perfect right away ...
// Better let the system shout at us, than ignoring fatal mistakes in code.
static if DEBUG_MODE then
private function IsNode takes integer node returns boolean
return HaveSavedBoolean(Inventory_GetTable(), INVENTORY_LIST_NODES, node)
endfunction
private function IsList takes integer list returns boolean
return HaveSavedBoolean(Inventory_GetTable(), INVENTORY_LIST_LISTS, list)
endfunction
endif
function Inventory_GetListByNode takes integer node returns integer
debug call ThrowError((node == 0), "InventoryCore", "Inventory_ListGetListByNode", "node", node, "Attempt to read null node!")
debug call ThrowError(not IsNode(node), "InventoryCore", "Inventory_ListGetListByNode", "node", node, "Attempt to read invalid node!")
return LoadInteger(Inventory_GetTable(), INVENTORY_LIST_LIST, node)
endfunction
private function GetList takes integer node returns integer
return LoadInteger(Inventory_GetTable(), INVENTORY_LIST_LIST, node)
endfunction
private function SetList takes integer node, integer list returns nothing
call SaveInteger(Inventory_GetTable(), INVENTORY_LIST_LIST, node, list)
endfunction
//
function Inventory_GetListFirstNode takes integer list returns integer
debug call ThrowError((list == 0), "InventoryCore", "Inventory_GetListFirstNode", "list", list, "Attempt to read null list!")
debug call ThrowError(not IsList(list), "InventoryCore", "Inventory_GetListFirstNode", "list", list, "Attempt to read invalid list!")
//
return LoadInteger(Inventory_GetTable(), INVENTORY_LIST_FIRST, list)
endfunction
private function GetFirst takes integer list returns integer
return LoadInteger(Inventory_GetTable(), INVENTORY_LIST_FIRST, list)
endfunction
private function SetFirst takes integer list, integer node returns nothing
call SaveInteger(Inventory_GetTable(), INVENTORY_LIST_FIRST, list, node)
endfunction
//
function Inventory_GetListLastNode takes integer list returns integer
debug call ThrowError((list == 0), "InventoryCore", "Inventory_GetListLastNode", "list", list, "Attempt to read null list!")
debug call ThrowError(not IsList(list), "InventoryCore", "Inventory_GetListLastNode", "list", list, "Attempt to read invalid list!")
//
return LoadInteger(Inventory_GetTable(), INVENTORY_LIST_LAST, list)
endfunction
private function GetLast takes integer list returns integer
return LoadInteger(Inventory_GetTable(), INVENTORY_LIST_LAST, list)
endfunction
private function SetLast takes integer list, integer node returns nothing
call SaveInteger(Inventory_GetTable(), INVENTORY_LIST_LAST, list, node)
endfunction
//
function Inventory_GetListNextNode takes integer node returns integer
debug call ThrowError((node == 0), "InventoryCore", "Inventory_GetListNextNode", "node", node, "Attempt to read null node!")
debug call ThrowError(not IsNode(node), "InventoryCore", "Inventory_GetListNextNode", "node", node, "Attempt to read invalid node for !" + I2S(GetList(node)))
//
return LoadInteger(Inventory_GetTable(), INVENTORY_LIST_NEXT, node)
endfunction
private function GetNext takes integer node returns integer
return LoadInteger(Inventory_GetTable(), INVENTORY_LIST_NEXT, node)
endfunction
private function SetNext takes integer node, integer nextNode returns nothing
call SaveInteger(Inventory_GetTable(), INVENTORY_LIST_NEXT, node, nextNode)
endfunction
//
function Inventory_GetListPrevNode takes integer node returns integer
debug call ThrowError((node == 0), "InventoryCore", "Inventory_GetListPrevNode", "node", node, "Attempt to read null node!")
debug call ThrowError(not IsNode(node), "InventoryCore", "Inventory_GetListPrevNode", "node", node, "Attempt to read invalid node!")
//
return LoadInteger(Inventory_GetTable(), INVENTORY_LIST_PREV, node)
endfunction
private function GetPrev takes integer node returns integer
return LoadInteger(Inventory_GetTable(), INVENTORY_LIST_PREV, node)
endfunction
private function SetPrev takes integer node, integer prevNode returns nothing
call SaveInteger(Inventory_GetTable(), INVENTORY_LIST_PREV, node, prevNode)
endfunction
private function AllocateList takes nothing returns integer
local integer list = GetFirst(0)
if (0 == list) then
// Pffffff.
debug call ThrowError(lists == 2147483648, "InventoryCore", "AllocateList", "lists", 0, "Overflow!")
//
set lists = lists + 1
set list = lists
else
call SetFirst(0, GetFirst(list))
endif
call SetFirst(list, 0)
debug call SaveBoolean(Inventory_GetTable(), INVENTORY_LIST_LISTS, list, true)
return list
endfunction
private function AllocateNode takes nothing returns integer
local integer node = GetNext(0)
if (0 == node) then
// Pffffff.
debug call ThrowError(nodes == 2147483648, "InventoryCore", "AllocateNode", "nodes", 0, "Overflow!")
//
set nodes = nodes + 1
set node = nodes
else
call SetNext(0, GetNext(node))
endif
debug call SaveBoolean(Inventory_GetTable(), INVENTORY_LIST_NODES, node, true)
return node
endfunction
function Inventory_EnqueueToList takes integer list returns integer
local integer node = AllocateNode()
debug call ThrowError((list == 0), "InventoryCore", "Inventory_ListEnqueue", "list", list, "Attempted to enqueue on to null list.")
debug call ThrowError(not IsList(list) ,"InventoryCore", "Inventory_ListEnqueue", "list", list, "Attempted to enqueue on to invalid list.")
call SetList(node, list)
if (0 == GetFirst(list)) then
call SetFirst(list, node)
call SetLast(list, node)
call SetPrev(node, 0)
else
call SetNext(GetLast(list), node)
call SetPrev(node, GetLast(list))
call SetLast(list, node)
endif
call SetNext(node, 0)
return node
endfunction
function Inventory_RemoveNodeFromList takes integer node returns nothing
local integer list = GetList(node)
debug call ThrowError(node == 0, "InventoryCore", "Inventory_ListRemoveNode", "list", list, "Attempted to remove null node!")
debug call ThrowError(not IsNode(node), "InventoryCore", "Inventory_ListRemoveNode", "list", list, "Attempted to remove invalid node [" + I2S(node) + "]!")
debug call RemoveSavedBoolean(Inventory_GetTable(), INVENTORY_LIST_NODES, node)
call SetList(node, 0)
if 0 == GetPrev(node) then
call SetFirst(list, GetNext(node))
else
call SetNext(GetPrev(node), GetNext(node))
endif
if (0 == GetNext(node)) then
call SetLast(list, GetPrev(node))
else
call SetPrev(GetNext(node), GetPrev(node))
endif
call SetNext(node, GetNext(0))
call SetNext(0, node)
endfunction
private function ReleaseNodes takes integer list returns nothing
local integer node = GetFirst(list)
local integer temp
loop
exitwhen 0 == node
set temp = GetNext(node)
call Inventory_RemoveNodeFromList(node)
set node = temp
endloop
endfunction
private function Inventory_DestroyList takes integer list returns nothing
debug call ThrowError(list == 0, "InventoryCore", "Inventory_DestroyList", "list", list, "Attempted to destroy a null list!")
debug call ThrowError(not IsList(list), "InventoryCore", "Inventory_DestroyList", "list", list, "Attempted to destroy an invalid list!")
call ReleaseNodes(list)
if 0 != GetFirst(list) then
call SetNext(GetLast(list), GetNext(0))
call SetNext(0, GetFirst(list))
call SetLast(list, 0)
endif
call SetFirst(list, GetFirst(0))
call SetFirst(0, list)
debug call RemoveSavedBoolean(Inventory_GetTable(), INVENTORY_LIST_LISTS, list)
endfunction
function Inventory_KeyForListExists takes integer parent, integer whichKey returns boolean
return HaveSavedInteger(Inventory_GetTable(), parent, whichKey)
endfunction
// Returns the list or creates one.
function Inventory_GetListByKey takes integer parent, integer whichKey returns integer
if not HaveSavedInteger(Inventory_GetTable(), parent, whichKey) then
call SaveInteger(Inventory_GetTable(), parent, whichKey, AllocateList())
endif
return LoadInteger(Inventory_GetTable(), parent, whichKey)
endfunction
function Inventory_DestroyListByKey takes integer parent, integer whichKey returns nothing
call Inventory_DestroyList(LoadInteger(Inventory_GetTable(), parent, whichKey))
call RemoveSavedInteger(Inventory_GetTable(), parent, whichKey)
endfunction
endlibrary
//TESH.scrollpos=166
//TESH.alwaysfold=0
// Inventory main code. Written by BPower. Version 1.6
library Inventory uses InventoryCore, InventoryUserSetup, CustomItem
//===================================================================================
// Inventory events.
// When an event trigger fires these values allow
// the action code to determine which event was dispatched.
// The functions listed below can be used to get information about the event.
//===================================================================================
globals
//=========================================
// Available Event Ids.
//=========================================
// RegisterInventoryEvent(eventId, code)
constant integer EVENT_INVENTORY_CREATED = 0
constant integer EVENT_INVENTORY_UNIT_EQUIP_ITEM = 1
constant integer EVENT_INVENTORY_UNIT_UNEQUIP_ITEM = 2
constant integer EVENT_INVENTORY_UNIT_PICKUP_ITEM = 3
constant integer EVENT_INVENTORY_UNIT_DROP_ITEM = 4
constant integer EVENT_INVENTORY_UNIT_PAWN_ITEM = 5
constant integer EVENT_INVENTORY_UNIT_BUY_ITEM = 6// ( Not working yet )
constant integer EVENT_INVENTORY_UNIT_TRADE_ITEM = 7// ( Not working yet )
//=======================================================
// Event Variables.
//=======================================================
// These variables update before an event fires.
// Get their information through the set of functions below.
private integer eventId = -1
private item eventItem = null
private integer eventIndex = 0
private unit eventTarget = null
private trigger array events
endglobals
//=========================================
// Inventory Trigger Interface.
//=========================================
// Returns the trigger handle for the specified event id.
// Implemented for usage with the native trigger interface.
// Use this function very carefully.
function GetInventoryTrigger takes integer eventId returns trigger
return events[eventId]
endfunction
//=======================================================
// Trigger Inventory Event API.
//=======================================================
// These functions only return their proper information when called
// from within a registered trigger action function.
// For incorrect usage they will either return "null", "-1" or "0".
// Returns the triggering Inventory instance.
constant function GetTriggerInventory takes nothing returns Inventory
return eventIndex
endfunction
// Returns the interacting item.
constant function Inventory_GetTriggerItem takes nothing returns item
return eventItem
endfunction
// Returns the interacting unit.
// For most events this function is equal to Inventory_GetTriggerUnit().
constant function Inventory_GetEventTargetUnit takes nothing returns unit
return eventTarget
endfunction
// Returns the most recent event id.
constant function Inventory_GetTriggerEventId takes nothing returns integer
return eventId
endfunction
// Returns the inventory owning unit.
// Specified for events where Inventory_GetEventTargetUnit()
// doesn't return the inventory source unit. ( Shop events )
constant function Inventory_GetTriggerUnit takes nothing returns unit
return GetTriggerInventory().source
endfunction
// Registers code to available Inventory events.
// Condition functions don't have to return a boolean in this case.
function RegisterInventoryEvent takes integer eventId, code func returns nothing
if events[eventId] == null then
set events[eventId] = CreateTrigger()
endif
call TriggerAddCondition(events[eventId], Condition(func))
endfunction
//=======================================================
// Event Trigger Evaluation.
//=======================================================
// Under normal circumstances Inventory events don't
// run into recursion issues. Yet the system is prepared
// to handle such specific cases without glitches.
// Inventory instance.
private function FireEvent takes integer id, integer index, item object, unit target returns nothing
// Save previous data.
local integer prevId = eventId
local item prevItem = eventItem
local integer prevIndex = eventIndex
local unit prevTarget = eventTarget
// Set current data and fire.
set eventId = id
set eventItem = object
set eventIndex = index
set eventTarget = target
if IsTriggerEnabled(events[id]) then// For "null" triggers function IsTriggerEnable(t) returns "false"
call TriggerEvaluate(events[id])// TriggerEvaluate would force disabled triggers to fire.
endif
// Restore previous data.
set eventId = prevId
set eventItem = prevItem
set eventIndex = prevIndex
set eventTarget = prevTarget
// Prevent handle leaks.
set prevItem = null
set prevTarget = null
endfunction
//=======================================================
// Module InventoryStruct.
//=======================================================
// Private interface for structs which should run code when an Inventory is created.
// Those structs must have a "static method onCreate takes Inventory new returns thistype".
module InventoryStruct
static method onCreateBefore takes nothing returns nothing
local Inventory created = GetTriggerInventory()
local boolean autostart = thistype.typeid != GetTradeTypeId() and thistype.typeid != GetMerchantTypeId()
local thistype this = thistype.onCreate(created)
if (0 != this) then
call created.addWindowOfType(thistype.typeid, this, autostart)
endif
endmethod
private static method onInit takes nothing returns nothing
call RegisterInventoryEvent(EVENT_INVENTORY_CREATED, function thistype.onCreateBefore)
endmethod
endmodule
//===================================================================================
// Inventory instance referencing.
// As an inventory belongs to a unit, you can get an instance
// by calling function GetUnitInventory(unit).
//===================================================================================
// Returns the Inventory instance for a unit.
// Returns 0 for units without inventory.
function GetUnitInventory takes unit source returns Inventory
return LoadInteger(Inventory_GetTable(), INVENTORY_KEY_UNIT_REFERENCE, GetHandleId(source))
endfunction
function ShowUnitInventory takes unit source, boolean flag returns nothing
local UIScreen screen = GetUnitInventory(source)
if screen.exists and screen.enabled != flag then
call screen.show(flag)
endif
endfunction
//===================================================================================
// Inventory interface sounds.
// These sound files run during various Inventory interface actions.
// For example when the backpack is full and therefore an item can't be picked up.
//===================================================================================
//=======================================================
// Sound File Registration.
//=======================================================
// Which sound file should be played is race based determined.
// Each sound file can and will be accessed via UISound.searchSoundFile(fileName)
// This function is called via module initializer on map initialization.
private function InitSoundFiles takes nothing returns nothing
call UISound.allocateIndex("Abilities\\Spells\\Items\\ResourceItems\\ReceiveGold.wav", 589, 10, .54)
call UISound.allocateIndex("Sound\\Interface\\QuestActivateWhat1.wav", 539, 10, .539)
call UISound.allocateIndex("Sound\\Interface\\PickUpItem.wav", 174, 10, .174)
call UISound.allocateIndex("Sound\\Interface\\HeroDropItem1.wav", 486, 10, .54)
//
call UISound.allocateIndex("Sound\\Interface\\Warning\\Orc\\GruntInventoryFull1.wav", 1567, 10, 1.)
call UISound.allocateIndex("Sound\\Interface\\Warning\\Orc\\GruntNoLumber1.wav", 1602, 10, 1.)
call UISound.allocateIndex("Sound\\Interface\\Warning\\Orc\\GruntNoGold1.wav", 1498, 10, 1.)
//
call UISound.allocateIndex("Sound\\Interface\\Warning\\Human\\KnightInventoryFull1.wav", 1498, 10, 1.)
call UISound.allocateIndex("Sound\\Interface\\Warning\\Human\\KnightNoGold1.wav", 1486, 10, 1.)
call UISound.allocateIndex("Sound\\Interface\\Warning\\Human\\KnightNoLumber1.wav", 1863, 10, 1.)
//
call UISound.allocateIndex("Sound\\Interface\\Warning\\Naga\\NagaInventoryFull1.wav", 2106, 10, 1.)
call UISound.allocateIndex("Sound\\Interface\\Warning\\Naga\\NagaNoGold1.wav", 1808, 10, 1.)
call UISound.allocateIndex("Sound\\Interface\\Warning\\Naga\\NagaNoLumber1.wav", 1576, 10, 1.)
//
call UISound.allocateIndex("Sound\\Interface\\Warning\\Nightelf\\SentinelInventoryFull1.wav", 1498, 10, 1.)
call UISound.allocateIndex("Sound\\Interface\\Warning\\NightElf\\SentinelNoGold1.wav", 1323, 10, 1.)
call UISound.allocateIndex("Sound\\Interface\\Warning\\NightElf\\SentinelNoLumber1.wav", 1501, 10, 1.)
//
call UISound.allocateIndex("Sound\\Interface\\Warning\\Undead\\NecromancerNoGold1.wav", 1805, 10, 1.)
call UISound.allocateIndex("Sound\\Interface\\Warning\\Undead\\NecromancerNoLumber1.wav", 1904, 10, 1.)
call UISound.allocateIndex("Sound\\Interface\\Warning\\Undead\\NecromancerInventoryFull1.wav", 1521, 10, 1.)
endfunction
//===================================================================================
// Special API.
// ( Place holder. No special API is defined yet )
//===================================================================================
// NO CONTENT.
//===================================================================================
// InventoryCell code.
// Handles and stores information for each
// interactable cell within an Inventory instance.
//===================================================================================
// The textmacro is located at the very bottom of the Inventory library.
//! runtextmacro INVENTORY_CELL_CODE("")
//===================================================================================
// Interface Inventory.
// Inventory extends UIScreen and therefore doesn't have own interactable cells.
// It provides the common object orientated API for Inventory instances
// and serves as main screen for the entire in-game interface surface.
//===================================================================================
struct Inventory extends UIScreen
//====================================
// Struct members.
//====================================
readonly unit source
//
readonly unit dummy
readonly real dummyY
readonly unit shop
// AddUnitAnimationProperties()
readonly string animation
// The global user setup defines a default font for all instances,
// you can change it at any time via this.setFont(font).
private integer fontType
method getFont takes nothing returns integer// font
return fontType
endmethod
method setFont takes integer newFont returns nothing
set fontType = newFont
endmethod
// The global user setup defines a default color for all instances,
// you can change it any time via this.setColor(ARGB)
private integer color
method getColor takes nothing returns ARGB
return color
endmethod
method setColor takes integer newColor returns nothing
set color = newColor
endmethod
//===================================================================================
// Text messages and effects for inventories.
// The following methods help to create a good
// ambience when using the Inventory interface in-game.
//===================================================================================
//=============================================
// Inventory Sounds. ( Run for a local player)
//=============================================
// Based on race handle ids.
// Reference: 1 - Human, 2 - Orc, 3 - Undead, 4 - Nightelf, 5 - Demon, 7 - Other, 11 - Naga
//
// API: .iHaveNoRoom(), .notEnoughLumber(), .notEnoughGold()
// Runs for an insufficient backpack space.
private method iHaveNoRoom takes nothing returns nothing
local integer id = GetHandleId(GetUnitRace(source))
if id == 1 or ((id != 11) and (id > 5)) then
call audio.searchSoundFile("Sound\\Interface\\Warning\\Human\\KnightInventoryFull1.wav", 10, 1.498)
elseif id == 2 then
call audio.searchSoundFile("Sound\\Interface\\Warning\\Orc\\GruntInventoryFull1.wav", 10, 1.567)
elseif id == 3 or id == 5 then
call audio.searchSoundFile("Sound\\Interface\\Warning\\Undead\\NecromancerInventoryFull1.wav", 10, 1.521)
elseif id == 4 then
call audio.searchSoundFile("Sound\\Interface\\Warning\\Nightelf\\SentinelInventoryFull1.wav", 10, 1.498)
elseif id == 11 then
call audio.searchSoundFile("Sound\\Interface\\Warning\\Naga\\NagaInventoryFull1.wav", 10, 2.106)
endif
endmethod
// Runs for an insufficient lumber resource state.
private method notEnoughLumber takes nothing returns nothing
local integer id = GetHandleId(GetUnitRace(source))
if id == 1 or ((id != 11) and (id > 5)) then
call audio.searchSoundFile("Sound\\Interface\\Warning\\Human\\KnightNoLumber1.wav", 10, 1.863)
elseif id == 2 then
call audio.searchSoundFile("Sound\\Interface\\Warning\\Orc\\GruntNoLumber1.wav", 10, 1.602)
elseif id == 3 or id == 5 then
call audio.searchSoundFile("Sound\\Interface\\Warning\\Undead\\NecromancerNoLumber1.wav", 10, 1.904)
elseif id == 4 then
call audio.searchSoundFile("Sound\\Interface\\Warning\\Nightelf\\SentinelNoLumber1.wav", 10, 1.501)
elseif id == 11 then
call audio.searchSoundFile("Sound\\Interface\\Warning\\Naga\\NagaNoLumber1.wav", 10, 1.575)
endif
endmethod
// Runs for an insufficient gold resource state.
private method notEnoughGold takes nothing returns nothing
local integer id = GetHandleId(GetUnitRace(source))
if id == 1 or ((id != 11) and (id > 5)) then
call audio.searchSoundFile("Sound\\Interface\\Warning\\Human\\KnightNoGold1.wav", 10, 1.486)
elseif id == 2 then
call audio.searchSoundFile("Sound\\Interface\\Warning\\Orc\\GruntNoGold1.wav", 10, 1.1498)
elseif id == 3 or id == 5 then
call audio.searchSoundFile("Sound\\Interface\\Warning\\Undead\\NecromancerNoGold1.wav", 10, 1.904)
elseif id == 4 then
call audio.searchSoundFile("Sound\\Interface\\Warning\\Nightelf\\SentinelNoGold1.wav", 10, 1.323)
elseif id == 11 then
call audio.searchSoundFile("Sound\\Interface\\Warning\\Naga\\NagaNoGold1.wav", 10, 1.808)
endif
endmethod
//====================================
// Custom Player Text Message.
//====================================
// Similar to a player text message,
// but it doesn't delete the current chat on screen.
//
// API: .timedMsg(msg, duration), .errorMsg(msg)
private real spam
private real lastMsgTime
private textsplat lastMsgObject
// Called upon hiding an inventory interface or when a new message is generated.
private method hideLastMsg takes nothing returns nothing
if UI_GetElapsedTime() < lastMsgTime then
call SetTextSplatColor(lastMsgObject, 0, 0, 0, 0)
call SetTextSplatLifespan(lastMsgObject, 0.)
endif
endmethod
method timedMsg takes string msg, real duration returns nothing
local real elapsed = UI_GetElapsedTime()
local ARGB color = getColor()
local textsplat t
if elapsed >= spam then
call hideLastMsg()// Let the previous text fade out invisible.
set spam = elapsed + .05// Spam protection.
set lastMsgTime = elapsed + duration
set t = CreateTextSplat(fontType)
call SetTextSplatFadepoint(t, duration - 1.)
call SetTextSplatLifespan(t, duration)
call SetTextSplatPermanent(t, false)
call SetTextSplatText(t, msg, INVENTORY_FONT_SIZE)
call SetTextSplatVisibility(t, enabled and GetLocalClient() == user)
call SetTextSplatColor(t, color.red, color.green, color.blue, color.alpha)
// Y position determined by try and error.
call SetTextSplatPos(t, centerX - t.width*.5, originY + height*.036, 0.)
set lastMsgObject = t
endif
endmethod
method errorMsg takes string msg returns boolean
call timedMsg(msg, 2.)
call audio.error()
return false
endmethod
//====================================
// Custom Player Text Box & Message.
//====================================
// This should definitly be outsourced to the UIPackage, but
// currently the API is not methodologically sound enough to work in any UI.
// The textbox is a bit more hardcoded than the rest. Get over it!
//
// API: .createTextBox(msg, posX, posY, width), .releaseTextBox()
private textsplat splat
private UIBorder box
private timer tmr
method releaseTextBox takes nothing returns nothing
local integer dex = 0
if (tmr != null) then
call ReleaseTimer(tmr)
call box.clear()
set tmr = null
call splat.unlock()
endif
endmethod
private static method fadeBox takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local textsplat t = splat
local real val
if (t.age >= t.fadepoint) then
set val = 1. - (t.age - t.fadepoint)/(t.lifespan - t.fadepoint)
if (val > 0.) then
call box.setColor(255, 255, 255, R2I(255*val))
else
call releaseTextBox()
endif
endif
endmethod
method createTextBox takes string msg, real posX, real posY, real width returns nothing
local integer dex = 1
local ARGB color = getColor()
local textsplat t = CreateTextSplat(fontType)
local integer end
// Free previous timer, textsplat and box.
call releaseTextBox()
// Prepare the text.
call WordWrap.create(msg, width, false)
set end = WordWrap.getCount()
set msg = WordWrap.getLine(0)
loop
exitwhen (dex >= end)
set msg = msg + "\n" + WordWrap.getLine(dex)
set dex = dex + 1
endloop
// TextSplat API.
call SetTextSplatColor(t, color.red, color.green, color.blue, color.alpha)
call SetTextSplatText(t, msg, 4.146479*1.5*scale)
call SetTextSplatPos(t, posX - t.width*.5, posY - t.height*.5, 0.)
call SetTextSplatVisibility(t, GetLocalClient() == user)
call SetTextSplatPermanent(t, false)
call SetTextSplatFadepoint(t, 2.)
call SetTextSplatLifespan(t, 4.)
call t.lock()// Prevent double free.
set splat = t
// Create a box with extra 32 units in both dimensions.
call Inventory_SetTooltipBoxBorderFields(source)
call box.construct(posX, posY, t.width + tileSize*.5, t.height + tileSize*.5, .65*scale).show(enabled and GetLocalClient() == user)
set tmr = NewTimerEx(this)
call TimerStart(tmr, .031250000, true, function thistype.fadeBox)
endmethod
//====================================
// Custom Item Tooltip.
//====================================
// The tooltip is looked up in library CustomItemTooltip
// and displayed line by line in a multiboard for a local player.
//
// API: .showItemTooltip(item)
private method displayTooltip takes item i returns nothing
local CustomItem id = GetHandleId(i)
local string text = id.getName()
local integer gold = id.getGoldCost()
local integer wood = id.getWoodCost()
local integer end = GetItemTooltipSize(id)
local integer dex = end
local string icon
local UIBoard mb
// Check for item charges.
if GetItemCharges(i) != 0 then
set text = text + " [" + I2S(GetItemCharges(i)) + "]"
endif
//
// Check for item resource costs.
if gold != 0 and wood != 0 then
set dex = dex + 3
elseif wood != 0 or gold != 0 then
set dex = dex + 2
endif
//
// Create the board with dex rows.
set mb = board.new(dex, 1)
set dex = 0
call MultiboardSetItemsWidth(mb.board, .16)
call MultiboardSetTitleText(mb.board, text)
//
loop
// Import the tooltip for "id" line by line.
exitwhen (dex == end)
set text = GetItemTooltipFragment(id, dex)
set icon = GetItemTooltipFragmentIcon(id, dex)
call mb.setText(dex, 0, text)
call mb.setIcon(dex, 0, icon)
call mb.setStyle(dex, 0, text != null, icon != null)
set dex = dex + 1
endloop
//
// Add extra lines for resource tooltips in the end.
if gold != 0 then
set dex = dex + 1
call mb.setText(dex, 0, "Sell Value: " + I2S(gold))
call mb.setIcon(dex, 0, INVENTORY_RESOURCE_GOLD_ICON)
call mb.setStyle(dex, 0, true, true)
endif
if wood != 0 then
set dex = dex + 1
call mb.setText(dex, 0, "Sell Value: " + I2S(wood))
call mb.setIcon(dex, 0, INVENTORY_RESOURCE_WOOD_ICON)
call mb.setStyle(dex, 0, true, true)
endif
//
// And finally show it to the player.
call MultiboardMinimize(mb.board, false)
endmethod
method showItemTooltip takes item i returns boolean
if i == null then
call board.release()
return false
endif
call displayTooltip(i)
return true
endmethod
//====================================
// Item Fxs & Animation Properties.
//====================================
// These informations update when equipping or unequipping an item.
// Run for the source unit and for the dummy ( if a dummy is set ).
//
// API: .addFx(item), .removeFx(item)
private Table effects
method removeFx takes item i returns nothing
local integer itemId = GetItemTypeId(i)
local integer id = GetCustomItemFxAbilityId(itemId)
local string ani = GetCustomItemAnimationProperty(itemId)
//
// Check for added effect handles.
if effects.has(itemId) then
set effects[itemId] = effects[itemId] - 1
if (effects[itemId] <= 0) then
call DestroyEffect(effects.effect[itemId])
call effects.handle.remove(itemId)
call effects.remove(itemId)
// Check for the dummy portrait.
if (dummy != null) then
call DestroyEffect(effects.effect[-itemId])
call effects.handle.remove(-itemId)
endif
endif
endif
//
// Check for added abilities to display a complex fx.
if effects.has(-itemId) then
set effects[-itemId] = effects[-itemId] - 1
if (effects[-itemId] <= 0) then
call effects.remove(itemId)
call UnitRemoveAbility(source, id)
if dummy != null then
call UnitRemoveAbility(dummy, id)
endif
endif
endif
//
// Check for the animation properties.
// Uses the "real" table field, because I'm running out of integers child keys :)!
if effects.real.has(itemId) then
set effects.real[itemId] = effects.real[itemId] - 1
if (effects.real[itemId] <= 0.5) then
call effects.real.remove(itemId)
call AddUnitAnimationProperties(source, ani, false)
if dummy != null then
call AddUnitAnimationProperties(dummy, ani, false)
endif
endif
endif
endmethod
method addFx takes item i returns nothing
local integer itemId = GetItemTypeId(i)
local integer id = GetCustomItemFxAbilityId(itemId)
local string pos = GetCustomItemFxPos(itemId)
local string ani = GetCustomItemAnimationProperty(itemId)
local string path = GetCustomItemFxFilePath(itemId)
//
// Effects via string path.
if (path != "") and (path != null) then
if not effects.has(itemId) then
if (GetLocalClient() != user) then
set path = ""
endif
set effects.effect[itemId] = AddSpecialEffectTarget(path, source, pos)
if (dummy != null) then
set effects.effect[-itemId] = AddSpecialEffectTarget(path, dummy, pos)
endif
endif
set effects[itemId] = effects[itemId] + 1
endif
//
// Animation properties.
if (ani != "") and (ani != null) then
if not effects.real.has(itemId) then
set animation = ani
call AddUnitAnimationProperties(dummy, ani, true)
if (dummy != null) then
call AddUnitAnimationProperties(source, ani, true)
endif
endif
set effects.real[itemId] = effects.real[itemId] + 1
endif
//
// Effects via ability.
if (id != 0) then
if not effects.has(-itemId) and UnitAddAbility(source, id) then
call UnitMakeAbilityPermanent(source, true, id)
if (dummy != null) then
call UnitAddAbility(dummy, id)
call UnitMakeAbilityPermanent(dummy, true, id)
endif
endif
set effects[-itemId] = effects[-itemId] + 1
endif
endmethod
//===================================================================================
// Core API for items and units.
// It's as close as possible to the
// native API for unit and item objects.
//===================================================================================
//====================================
// Cell Selection.
//====================================
// Basically always called when
// clicking a trackable of the inventory interface.
readonly InventoryCell selected
readonly destructable selector
method deselect takes nothing returns nothing
if selector != null then
call RemoveDestructable(selector)
set selector = null
endif
set selected = 0
endmethod
method select takes InventoryCell cell returns nothing
local UIButton temp = cell.getButton()
call deselect()
set selected = cell
set selector = CreateDestructableZ(INVENTORY_CELL_SELECTOR_ID, temp.x, temp.y, temp.z, temp.facing, temp.scale*INVENTORY_CELL_SELECTOR_SCALE*scale, 0)
call ShowDestructable(selector, enabled and GetLocalClient() == user)
//
debug call ThrowWarning(not cell.exists, "Inventory", "select", "cell", cell, "Attempt to select invalid cell instance!")
endmethod
//====================================
// Shop Detection.
//====================================
private method getClosestShop takes nothing returns nothing
local real dist = INVENTORY_MAXIMUM_SHOP_RANGE*INVENTORY_MAXIMUM_SHOP_RANGE + 1.
local real x = GetUnitX(source)
local real y = GetUnitY(source)
local group temp = CreateGroup()
local real dx
local real dy
local real d
local unit u
call GroupEnumUnitsInRange(temp, x, y, INVENTORY_MAXIMUM_SHOP_RANGE, null)
loop
set u = FirstOfGroup(temp)
exitwhen u == null
call GroupRemoveUnit(temp, u)
// Shop purchase item ability.
if IsUnitAlly(u, user) and GetUnitAbilityLevel(u, 'Apit') != 0 then
if IsUnitVisible(u, user) and not IsUnitType(u, UNIT_TYPE_DEAD) then
set dx = x - GetUnitX(u)
set dy = y - GetUnitY(u)
set d = dx*dx + dy*dy
if d < dist then
set dist = d
set shop = u
endif
endif
endif
endloop
call DestroyGroup(temp)
if shop != null then
call getWindowOfType(GetMerchantTypeId()).show(true)
endif
set temp = null
endmethod
//====================================
// Searching Cells Of Struct Type.
//====================================
// This function helps us to find empty cells for an item
// inside a specific struct interface.
// Returns 0 if no matching cell was found.
private method searchCell takes integer structId, item i returns InventoryCell
local UIWindow window = getWindowOfType(structId)
local integer end = window.getButtonCount()
local integer dex = 0
local InventoryCell cell
loop
exitwhen dex == end
set cell = window.getButton(dex).data
if cell.enabled and cell.getItem() == null and cell.matchesItem(i) then
return cell
endif
set dex = dex + 1
endloop
return 0
endmethod
// This function does the same as searchCell, but only for the equipment interface.
// It has a much, much better performance, which is essential for equipment related API ( see below ).
//
private Table equipment // For "true" the function considers cells which are not empty.
private method searchEquipCell takes item whichItem, boolean forceAction returns InventoryCell
local integer pos = GetCustomItemClass(whichItem) + 1
local integer end = equipment[-pos]
local integer dex = 0
local integer forced = 0
local InventoryCell cell
//
set pos = pos*JASS_MAX_ARRAY_SIZE
loop
exitwhen dex == end
set cell = equipment[pos + dex]
if cell.enabled then
if cell.getItem() == null then
return cell
endif
set forced = cell
endif
set dex = dex + 1
endloop
if forceAction then
return forced
endif
return 0
endmethod
// Internal function wrapper to search an suit-able cell for an item.
private method searchCellForItem takes UIWindow structId, item i, boolean forceAction returns InventoryCell
debug call ThrowWarning(i == null, "Inventory", "searchCellForItem", "object", this, "Invalid item handle ( null )!")
//
if not hasWindowOfType(structId) then
return 0
// Equipment has an individual lookup algorithm for better performance.
elseif structId == GetEquipmentTypeId() then
return searchEquipCell(i, forceAction)
endif
return searchCell(structId, i)
endmethod
//====================================
// Swapping Cell Content.
//====================================
// Works only for cells of the same struct.
method swapCellContents takes InventoryCell a, InventoryCell b returns boolean
if a.getTypeId() == b.getTypeId() and b.enabled and a.enabled then
if ((a.getItem() == null) or b.matchesItem(a.getItem())) and ((b.getItem() == null) or a.matchesItem(b.getItem())) then
call a.swap(b, enabled)
return true
endif
endif
//
debug call ThrowWarning(not a.exists, "Inventory", "swapCellContents", "a", a, "Attempt to swap to an invalid cell instance!")
debug call ThrowWarning(not b.exists, "Inventory", "swapCellContents", "b", b, "Attempt to swap to an invalid cell instance!")
return false
endmethod
//===============================
// Equipment realted API.
//===============================
// For a specific item handle.
method unitHasItemEquipped takes item whichItem returns boolean
local integer pos = GetCustomItemClass(whichItem) + 1
local integer end = equipment[-pos]
local integer dex = 0
set pos = pos*JASS_MAX_ARRAY_SIZE
loop
exitwhen (dex == end)
if (InventoryCell(equipment[pos + dex]).getItem() == whichItem) then
return true
endif
set dex = dex + 1
endloop
return false
endmethod
// For an item of type id.
method unitHasItemIdEquipped takes integer itemId returns boolean
local integer pos = GetCustomItemIdClass(itemId) + 1
local integer end = equipment[-pos]
local integer dex = 0
set pos = pos*JASS_MAX_ARRAY_SIZE
loop
exitwhen (dex == end)
if (InventoryCell(equipment[pos + dex]).data.getItemId() == itemId) then
return true
endif
set dex = dex + 1
endloop
return false
endmethod
//===========================================
// Mimic Native Unit Item API & Specific API
//===========================================
//
// Any interface related.
// .unitRemoveItem(item) - UnitRemoveItem(unit, item)
// Equipment related.
// .unitUnequipItemToSlot(item, slot)
// .method unitEquipItemToSlot(item, slot, forceAction)
// .method unitEquipItem(item)
// Backpack related.
// .method unitAddItemToSlot(item, slot)
// .method unitAddItem(item)
// Merchant related.
// .method unitPawnItem(item, shop)
// .method unitBuyItem(item, shop) ( Not implemented yet )
// Player player interaction related.
// .method unitTradeItem(item, otherInventory) ( Not implemented yet )
// Item related
// .method mergeItems(item1, item2)
// .method socketItem(item, gem) ( Not implemented yet )
// .method unsocketItem(item) ( Not implemented yet )
method getItemSlotIndex takes item whichItem returns integer
local integer dex = UnitInventorySize(source)
loop
exitwhen whichItem == null
exitwhen dex <= 0
set dex = dex - 1
if UnitItemInSlot(source, dex) == whichItem then
return dex
endif
endloop
return -1
endmethod
method unitRemoveItem takes item whichItem returns boolean
local CustomItem id = GetCustomItem(whichItem)
local InventoryCell cell = id.getCell()
// Check cell and item owner.
if cell.exists and id.getOwner() == source then
// Check if the item is equipped.
if cell.getTypeId() == GetEquipmentTypeId() then
// Check for twohand item.
if IsCustomItemTwohand(whichItem) then
call InventoryCell.removeShadow(whichItem)
endif
call FireEvent(EVENT_INVENTORY_UNIT_UNEQUIP_ITEM, this, whichItem, source)
endif
call cell.clear()
// Place and fire. Automatically clears the item owner.
call id.placeInMap(GetUnitX(source), GetUnitY(source))
call FireEvent(EVENT_INVENTORY_UNIT_DROP_ITEM, this, whichItem, source)
return true
endif
return false
endmethod
method unitUnequipItemToSlot takes item whichItem, InventoryCell slot returns boolean
local CustomItem object = GetHandleId(whichItem)
local InventoryCell cell = object.getCell()
// Invalid operation.
if ((object.getOwner()) != source) or (cell.getTypeId() != GetEquipmentTypeId()) or (whichItem == null) then
return false
endif
// slot == 0, Try again.
if (slot == 0) then
set slot = searchCellForItem(GetBackpackTypeId(), whichItem, false)
if (slot == 0) then
return unitRemoveItem(whichItem)
endif
endif
// Check if it's a twohand item.
if IsCustomItemTwohand(whichItem) then
call InventoryCell.removeShadow(whichItem)
endif
// Move and fire.
call slot.moveItem(whichItem, enabled)
call RemoveUnitItemAffixes(source, whichItem)
call removeFx(whichItem)
call FireEvent(EVENT_INVENTORY_UNIT_UNEQUIP_ITEM, this, whichItem, source)
return true
endmethod
// Equips items to the unit.
method unitEquipItemToSlot takes item whichItem, InventoryCell slot, boolean forceAction returns boolean
local InventoryCell cell
local CustomItem object = GetHandleId(whichItem)
// Invalid operation.
if (slot.getTypeId() != GetEquipmentTypeId()) or (object.getOwner() != source) then
debug call ThrowWarning((object.getOwner() != source), "Inventory", "unitEquipItemToSlot", "owner", this, "Can't equip an item of different owners!")
return false
//
// Missing requirements.
elseif not equipment.has(-(GetCustomItemClass(whichItem) + 1)) then
return errorMsg(GetUnitName(source) + ": I can not wear " + object.getName() + "!")
//
// Invalid cell.
elseif not slot.matchesItem(whichItem) then
return errorMsg(object.getName() + " does not fit in a " + slot.class.name + " slot!")
endif
// Check if it's a twohand item.
if (IsCustomItemTwohand(whichItem)) then
set slot.enabled = false
set cell = searchCellForItem(GetEquipmentTypeId(), whichItem, forceAction)
set slot.enabled = true
if (cell == 0) then
return false
endif
// Check cell content.
if (cell.getItem() != null) then
call unitUnequipItemToSlot(cell.getItem(), searchCellForItem(GetBackpackTypeId(), cell.getItem(), false))
endif
call cell.addShadow(whichItem, enabled)
endif
set cell = object.getCell()// Get the source cell.
call unitUnequipItemToSlot(slot.getItem(), cell)
// Move and fire.
call slot.moveItem(whichItem, enabled)
//
call AddUnitItemAffixes(source, whichItem)
call addFx(whichItem)
call FireEvent(EVENT_INVENTORY_UNIT_EQUIP_ITEM, this, whichItem, source)
return true
endmethod
// Does not force the equip process.
method unitEquipItem takes item whichItem returns boolean
return unitEquipItemToSlot(whichItem, searchCellForItem(GetEquipmentTypeId(), whichItem, false), false)
endmethod
// Only accepts Backpack cells as slot.
method unitAddItemToSlot takes item whichItem, InventoryCell slot returns boolean
local CustomItem id = GetCustomItem(whichItem)
local thistype temp = GetUnitInventory(id.getOwner())
// Invalid operation. Happens when the inventory is full.
if (slot.getTypeId() != GetBackpackTypeId()) then
return false
endif
// Check item.
if id.isCustom() then
// Check slot.
if slot.enabled and slot.getItem() == null and slot.matchesItem(whichItem) then
// Check the previous item owner.
// Analogous to UnitAddItem this method
// must work if the owner is not this.source.
if temp != 0 then
call temp.unitRemoveItem(whichItem)// Fires all events for temp.
endif
call slot.moveItem(whichItem, enabled)
call id.setOwner(source)
call FireEvent(EVENT_INVENTORY_UNIT_PICKUP_ITEM, this, whichItem, source)
return true
endif
endif
//
debug call ThrowWarning(not IsCustomItem(whichItem), "Inventory", "unitAddItemToSlot", "whichItem", this, GetItemName(whichItem) + " is not registered as CustomItem!")
return false
endmethod
// Returns false, if no backpack cell is available.
method unitAddItem takes item whichItem returns boolean
return unitAddItemToSlot(whichItem, searchCellForItem(GetBackpackTypeId(), whichItem, false))
endmethod
// UNDER CONSTRUCTION.
method unitPawnItem takes item whichItem, unit shop returns boolean
local CustomItem object = GetHandleId(whichItem)
// Check pawn condition.
if (object.isItemPawnPossible(source, shop)) then
// Fire other events.
call unitRemoveItem(whichItem)
// Pawn item and fire.
call object.pawnItem(source)
call FireEvent(EVENT_INVENTORY_UNIT_PAWN_ITEM, this, whichItem, shop)
// No one took the item within the event. Remove it.
if (object.getOwner() == null) then
call RemoveItem(whichItem)
endif
return true
endif
return false
endmethod
// UNDER CONSTRUCTION.
method unitBuyItem takes item whichItem, unit shop returns boolean
local CustomItem object = GetHandleId(whichItem)
// Check buy condition.
if (object.isItemBuyPossible(source, shop)) then
call FireEvent(EVENT_INVENTORY_UNIT_BUY_ITEM, this, whichItem, shop)
call unitAddItem(whichItem)
return true
endif
return false
endmethod
// UNDER CONSTRUCTION.
method unitTradeItem takes item whichItem, thistype partner returns boolean
return false
endmethod
// UNDER CONSTRUCTION.
method socketItem takes item whichItem, item gem returns boolean
return false
endmethod
// UNDER CONSTRUCTION.
method unsocketItem takes item whichItem returns boolean
return false
endmethod
method mergeItems takes item target, item source returns boolean
if IsItemStackableWithItem(target, source) then
call CustomItem[target].mergeCharges(source)
return true
endif
return false
endmethod
//=============================================
// Inventory Creator, Destructor & Related API.
//=============================================
// Must be called from each interface struct, which should have interactable cells.
method initInventoryCells takes UIWindow window returns nothing
local integer index = 0
local integer size = window.getButtonCount()
loop
exitwhen (index == size)
call InventoryCell.create(window.getButton(index))
set index = index + 1
endloop
endmethod
// Re-structures the equipment interface.
// Boosts the lookup time drastically for useful Equipment related API.
// JASS_MAX_ARRAY_SIZE is used as offset between two classes.
private static integer tempInventory = 0
private static method structure takes nothing returns nothing
local thistype this = thistype.tempInventory
local integer index
local integer child
local integer size
local integer space
local UIWindow window
local ItemClass class
local InventoryCell cell
if not (hasWindowOfType(GetEquipmentTypeId())) then
return
endif
set index = 0
set equipment = Table.create()
set window = getWindowOfType(GetEquipmentTypeId())
set size = window.getButtonCount()
loop
exitwhen index == size
set cell = window.getButton(index).data
set class = cell.class
loop
set child = class + 1
set space = equipment[-child]
set equipment[child*JASS_MAX_ARRAY_SIZE + space] = cell
set equipment[-child] = space + 1
set class = class.parent
exitwhen class.root
endloop
set index = index + 1
endloop
endmethod
// Users should not use these methods, because only the dedicated wrapper function check for misuse.
// Go with CreateInventory(), DestroyInventory(), AddUnitInventoryDummy(). I also do so.
method createDummyPortrait takes integer dummyId, real extraY returns nothing
debug local string error = "Failed to create a dummy for " + GetUnitName(source) + " from unit type id ['" + A2S(dummyId) + "']!"
//
local boolean prev = ToogleUnitIndexer(false)
local unit temp = CreateUnit(user, dummyId, WorldBounds.maxX, WorldBounds.maxY, 1.)
call ToogleUnitIndexer(prev)
call SetUnitFacing(temp, 0.)
call PauseUnit(temp, true)
call ShowUnit(temp, false)
call UnitAddAbility(temp, 'Amrf')
call UnitAddAbility(temp, 'Aloc')
call UnitAddAbility(temp, 'Abun')
if (GetLocalClient() != user) then
call SetUnitVertexColor(temp, 0, 0, 0, 0)
endif
set dummyY = extraY
set dummy = temp
set temp = null
//
debug call ThrowWarning(GetUnitTypeId(dummy) == 0, "Inventory", "createDummyPortrait", "dummy", this, error)
endmethod
// Actual creator function. Use function wrapper CreateInventory().
static method construct takes unit sourceUnit, real originX, real originY, real width, real height returns thistype
local thistype this = thistype.create(GetOwningPlayer(sourceUnit), originX, originY, width, height)
set source = sourceUnit
set fontType = Inventory_GetDefaultFont()
set color = Inventory_GetDefaultFontColor()
set box = UIBorder.create()
set effects = Table.create()
//
// Reference the inventory instance.
call SaveInteger(Inventory_GetTable(), INVENTORY_KEY_UNIT_REFERENCE, GetHandleId(sourceUnit), this)
//
// Init all interfaces for Inventory.
call FireEvent(EVENT_INVENTORY_CREATED, this, null, null)
//
// Restructure all equipment cells. ForForce to avoid hitting an OP limit.
set tempInventory = this
call ForForce(bj_FORCE_PLAYER[0], function thistype.structure)
return this
endmethod
// onDestroy has an inappropriate timing for Inventory,
// because it runs before the window destructor.
//
// The inventory instance is reference on the unit handle id.
// We release the handle last, so all windows can be destroyed properly.
method pastDestroy takes nothing returns nothing
call RemoveSavedInteger(Inventory_GetTable(), INVENTORY_KEY_UNIT_REFERENCE, GetHandleId(source))
call effects.destroy()
call equipment.destroy()
call RemoveUnit(dummy)
call box.destroy()
call deselect()
set dummy = null
set source = null
endmethod
//===============================================
// Bridge Between Native And Custom Inventory UI.
//===============================================
// We must evaluate if an item can be moved between the two UIs.
private static method preSwapCheckItem takes item i, InventoryCell slot returns boolean
if i == null then
return true
elseif not IsCustomItem(i) or not IsItemCustomNativeCompatible(i) then
return false
endif
return slot.matchesItem(i)
endmethod
// UnitDropItemSlot
method swapCustomNative takes InventoryCell customCell, InventoryCell nativeCell, integer slot returns boolean
local item customItem = customCell.getItem()
local item nativeItem = nativeCell.getItem()
// Check all swapping conditions.
local boolean condition = nativeCell.enabled and customCell.enabled and slot > -1
set condition = preSwapCheckItem(customItem, nativeCell) and preSwapCheckItem(nativeItem, customCell)
// All conditions have run successfully.
if condition then
// Free custom slot.
if customItem != null then
call unitRemoveItem(customItem)
endif
// Free native slot and add to custom slot.
if nativeItem != null then
call unitAddItemToSlot(nativeItem, customCell)
endif
// Add to native slot.
call DisableTrigger(GetPlayerUnitEventTrigger(EVENT_PLAYER_UNIT_PICKUP_ITEM))
if UnitAddItem(source, customItem) then
call UnitDropItemSlot(source, customItem, slot)
endif
call EnableTrigger(GetPlayerUnitEventTrigger(EVENT_PLAYER_UNIT_PICKUP_ITEM))
endif
set customItem = null
set nativeItem = null
return condition
endmethod
//====================================
// Sync With The Native Inventory UI.
//====================================
// Calling method sync from the trigger action function,
// will lead to false results, thus the native and
// custom inventory UI are syncronized
// with a 0 second timeout timer on:
// • EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER ( orders within the native UI ).
// • EVENT_PLAYER_UNIT_DROP_ITEM.
//
// And directly syncronized on:
// • EVENT_PLAYER_UNIT_PICKUP_ITEM.
// • ShowUnitInventory(unit, true)
private boolean syncReady
private thistype syncNext // Singly linked list.
private method sync takes nothing returns nothing
local UIWindow window = getWindowOfType(GetNativeInventoryTypeId())
local integer dex = IMinBJ(window.getButtonCount(), UnitInventorySize(source))
local item temp
loop
exitwhen dex <= 0
set dex = dex - 1
set temp = UnitItemInSlot(source, dex)
if temp == null then
call InventoryCell(window.getButton(dex).data).clear()
else
call InventoryCell(window.getButton(dex).data).moveItem(temp, enabled)
endif
endloop
set temp = null
set syncReady = true
endmethod
// 0 seconds timer timeout.
private static method syncWait takes nothing returns nothing
local thistype this = thistype(0).syncNext
call DestroyTimer(GetExpiredTimer())
loop
exitwhen 0 == this
call sync()
set this = syncNext
endloop
set thistype(0).syncNext = 0
endmethod
private static method startSync takes thistype this returns nothing
if not exists or not enabled or not syncReady then
return
endif
set syncReady = false
if 0 == thistype(0).syncNext then
call TimerStart(CreateTimer(), 0, false, function thistype.syncWait)
endif
set syncNext = thistype(0).syncNext
set thistype(0).syncNext = this
endmethod
//====================================
// Stub Method On Show.
//====================================
// Inventory is a child struct of UIScreen,
// therefore it evaluates an onShow stub method
// each time instance.show(boolean) is called.
private method onShow takes boolean show returns nothing
set shop = null
if show then
call sync()
if hasWindowOfType(GetMerchantTypeId()) then
call getClosestShop()
endif
else
call releaseTextBox()
call hideLastMsg()
call deselect()
endif
endmethod
//===========================================
// Native Unit Item Trigger Events.
//===========================================
// These events are essential for Inventory,
// as items are allocated and moved as action
// function of their event triggers.
// Thus disabling a player unit event trigger
// will make Inventory do nothing when the event occurs.
// EVENT_PLAYER_UNIT_PICKUP_ITEM.
private static method onPickUpItem takes nothing returns nothing
local item picked = GetManipulatedItem()
local thistype this
if GetCustomItem(picked).isCustom() then
set this = GetUnitInventory(GetTriggerUnit())
if exists then// Does this unit has an inventory.
if not IsItemPurchasable(picked) then
if not unitAddItem(picked) then
call iHaveNoRoom()
call UnitRemoveItem(source, picked)
endif
elseif enabled and syncReady then
call sync()
endif
endif
endif
set picked = null
endmethod
// EVENT_PLAYER_UNIT_DROP_ITEM.
private static method onDropItem takes nothing returns nothing
call startSync(GetUnitInventory(GetTriggerUnit()))
endmethod
// EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER.
private static method onTargetOrder takes nothing returns nothing
if GetIssuedOrderId() > 852001 and GetIssuedOrderId() < 852008 then
if GetOrderTargetItem() != null then
call GetCustomItem(GetOrderTargetItem())
call startSync(GetUnitInventory(GetTriggerUnit()))
endif
endif
endmethod
// NOT REQUIRED YET.
private static method onPawnItem takes nothing returns nothing
endmethod
private static method onSellItem takes nothing returns nothing
endmethod
private static method onUseItem takes nothing returns nothing
endmethod
private static method init takes nothing returns nothing
call InitSoundFiles()
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_PICKUP_ITEM, function thistype.onPickUpItem)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_PAWN_ITEM, function thistype.onPawnItem)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DROP_ITEM, function thistype.onDropItem)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SELL_ITEM, function thistype.onSellItem)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_USE_ITEM, function thistype.onUseItem)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function thistype.onTargetOrder)
endmethod
implement UIInit
endstruct
//===================================================================================
// Wrapper functions for safe Inventory API.
// Please always use these functions below.
//===================================================================================
function CreateInventory takes unit source, real originX, real originY returns Inventory
local boolean valid = (GetUnitTypeId(source) != 0) and (GetUnitInventory(source) == 0)
if (valid) then
return Inventory.construct(source, originX, originY, 1500, 1500/UI_PERFECT_SCREEN_RATIO)
endif
//
debug call ThrowWarning((GetUnitInventory(source) != 0), "Inventory", "CreateInventory", "", 0, GetUnitName(source) + " has already an inventory!")
debug call ThrowWarning((GetUnitTypeId(source) == 0), "Inventory", "CreateInventory", "", 0, "Invalid unit argument ( null )!")
return 0
endfunction
function AddUnitInventoryDummy takes unit source, integer dummyId, real dummyY returns nothing
local Inventory instance = GetUnitInventory(source)
local boolean valid = (instance.exists) and (instance.dummy == null)
if (valid) then
call instance.createDummyPortrait(dummyId, dummyY)
endif
//
debug call ThrowWarning((not instance.exists), "Inventory", "AddUnitInventoryDummy", "", 0, GetUnitName(source) + " has no inventory!")
debug call ThrowWarning((instance.exists and not valid), "Inventory", "AddUnitInventoryDummy", "dummy", instance, "This inventory has already a dummy unit!")
endfunction
function DestroyInventory takes unit source returns nothing
local Inventory instance = GetUnitInventory(source)
if (instance.exists) then
call instance.destroy()// Hides and destroys all interface windows.
call instance.pastDestroy()//
//
debug else
debug call ThrowWarning(true, "Inventory", "DestroyInventory", "", 0, GetUnitName(source) + " has no inventory!")
endif
endfunction
//==========================================================================================
// Struct InventoryCell handles each interactable cell in Inventory.
// It's limited to 8910 instances and therefore also limits the amount
// of maximum Inventory instances. More cells can be allocated by
// passing a struct space into the textmacro located at the top of the Inventory library.
// A table allocator could enable endless cell instances, but I hope you don't need it.
//==========================================================================================
//! textmacro INVENTORY_CELL_CODE takes MORE
struct InventoryCell $MORE$
// Tracks items which need two cells.
private static Table shadows
// Returns false for invalid cells.
method operator exists takes nothing returns boolean
return (icon != 0)
endmethod
readonly CustomItem data // Item in this cell. 0 for no item.
readonly ItemClass class // Class of this cell.
readonly UIButton icon // UIButton ( trackable & visual for this cell )
public boolean enabled // Status of this cell.
method getButton takes nothing returns UIButton
return icon
endmethod
method hasItem takes nothing returns boolean
return (data != 0)
endmethod
method getDescription takes nothing returns string
return class.name
endmethod
// CustomItem and InventoryCell are strongly linked with each other.
// Get a cell by CustomItem index. Returns 0 for no cell.
static method operator [] takes CustomItem index returns thistype
return index.getCell()
endmethod
// Get the struct this cell is located in.
method getTypeId takes nothing returns integer
return icon.typeId
endmethod
// Get the actual item handle. Returns "null" for no item in this cell.
method getItem takes nothing returns item
return data.getItem()
endmethod
// Called each time something happens in an Inventory.
method update takes boolean show returns nothing
local integer itemId = data.getItemId()
local integer iconId = GetCustomItemIconId(itemId)
local string path = GetCustomItemIconPath(itemId)
if data == 0 then
call icon.remove()
else
if path != "" and path != null then
call icon.addImage(path, show)
elseif iconId != 0 then
call icon.addDest(iconId, show)
elseif INVENTORY_ITEM_ICON_PATH != null and INVENTORY_ITEM_ICON_PATH != "" then
call icon.addImage(INVENTORY_ITEM_ICON_PATH, show)
elseif INVENTORY_ITEM_ICON_ID != 0 then
call icon.addDest(INVENTORY_ITEM_ICON_ID, show)
endif
//
// Reference to this cell in CustomItem.
call data.setCell(this, getTypeId() != GetNativeInventoryTypeId())
endif
endmethod
// Depreciated, but I don't know if still used somewhere.
method move takes integer it, boolean show returns nothing
debug call ThrowWarning(true, "Inventory", "InventoryCell", "move", 0, "Don't use method move!")
set data = it
call update(show)
endmethod
method swap takes thistype cell, boolean show returns nothing
local integer temp = data
set data = cell.data
set cell.data = temp
call cell.update(show)
call this.update(show)
endmethod
static method create takes UIButton node returns thistype
local thistype this = thistype.allocate()
set icon = node
set data = 0
set class = node.data
set enabled = true
set node.data = this
return this
endmethod
// Compare item which cell class.
public method matchesItem takes item i returns boolean
local CustomItem whichItem = GetHandleId(i)
local ItemClass leaf = class
local ItemClass itemClass = whichItem.getClass()
loop
exitwhen (itemClass == leaf) or (leaf.root)
set leaf = leaf.parent
endloop
return (leaf == itemClass) or (class.root)
endmethod
// Depreciated, but still here.
public method matches takes CustomItem id returns boolean
return matchesItem(id.getItem())
endmethod
// For twohand items.
method addShadow takes item whichItem, boolean flag returns nothing
local integer itemId = GetItemTypeId(whichItem)
local string file = GetCustomItemIconDISPath(itemId)
local integer dest = GetCustomItemIconDISId(itemId)
local integer id = GetHandleId(whichItem)
if (dest == 0) and (file != "") and (file != null) then
call icon.addImage(file, flag)
elseif (dest != 0) then
call icon.addDest(dest, flag)
elseif (INVENTORY_ITEM_DIS_ICON_ID != 0) then
call icon.addDest(INVENTORY_ITEM_DIS_ICON_ID, flag)
elseif (INVENTORY_ITEM_DIS_ICON_PATH != "") and (INVENTORY_ITEM_DIS_ICON_PATH != null) then
call icon.addImage(INVENTORY_ITEM_DIS_ICON_PATH, flag)
endif
set shadows[id] = this
set data = id
endmethod
static method removeShadow takes item whichItem returns nothing
local thistype this = shadows[GetHandleId(whichItem)]
if (this != 0) and (exists) then
call icon.remove()
set data = 0
call shadows.remove(GetHandleId(whichItem))
endif
endmethod
method clear takes nothing returns nothing
debug call ThrowError(not exists, "InventoryCell", "clear", "thistype", this, "This instance is not allocated!")
set data = 0
call icon.remove()
endmethod
method moveItem takes item i, boolean show returns nothing
local CustomItem ci = CustomItem[i]
local thistype node = ci.getCell()
debug call ThrowWarning(GetItemTypeId(i) == 0, "InventoryCell", "moveItem", "it", this, "Invalid item handle ( null )!")
if (node.exists) and (node.getItem() == i) then
call node.clear()
endif
set data = ci
call update(show)
endmethod
method destroy takes nothing returns nothing
if shadows.has(data) then
call removeShadow(data.getItem())
endif
call clear()
set enabled = false
set icon = 0
set class = 0
call deallocate()
endmethod
static method onRemove takes thistype this returns nothing
if (data != 0) then
call clear()
endif
endmethod
private static method onDestroyItem takes nothing returns nothing
local CustomItem ci = GetTriggerCustomItem()
local thistype this = ci.getCell()
if (exists) and (data == ci) then
call clear()
endif
endmethod
private static method init takes nothing returns nothing
call RegisterCustomItemEvent(EVENT_CUSTOM_ITEM_DESTROY, function thistype.onDestroyItem)
set shadows = Table.create()
endmethod
implement UIInit
endstruct
//! endtextmacro
endlibrary
//TESH.scrollpos=265
//TESH.alwaysfold=0
// CustomItem main code.
library CustomItem uses InventoryCore
//===================================================
// Design:
// CustomItem extends the native item handle.
// It has a "PUI" like indexing design while
// referencing items to their respective handle id.
// A cleanup check runs after each 16 items indexed.
//===================================================
//===================================================
// Events:
// On first indexing and cleanup
// a respective trigger event fires.
// - EVENT_CUSTOM_ITEM_CREATE
// - EVENT_CUSTOM_ITEM_DESTROY
//===================================================
//===================================================
// Restrictions:
// - ITEM_TYPE_POWERUP items can't be CustomItems.
// - ITEM_TYPE_PURCHASABLE items can be CustomItems,
// but not picked into the inventory interface.
//===================================================
//====================================================
// Struct id getters:
// Use these constant functions to get the
// unique struct id of structs
// from the inventory interface.
//==================================================
// Example: GetBackpackTypeId() returns the struct id for the backpack interface.
//! textmacro INVENTORY_STRUCT_ID_GETTER takes NAME
constant function Get$NAME$TypeId takes nothing returns integer
static if LIBRARY_Inventory$NAME$ then
return Inventory$NAME$.typeid
else
return 0
endif
endfunction
//! endtextmacro
//
//! runtextmacro INVENTORY_STRUCT_ID_GETTER("Equipment")
//! runtextmacro INVENTORY_STRUCT_ID_GETTER("Backpack")
//! runtextmacro INVENTORY_STRUCT_ID_GETTER("Merchant")
//! runtextmacro INVENTORY_STRUCT_ID_GETTER("Controll")
//! runtextmacro INVENTORY_STRUCT_ID_GETTER("Dummy")
//! runtextmacro INVENTORY_STRUCT_ID_GETTER("Trade")
//! runtextmacro INVENTORY_STRUCT_ID_GETTER("NativeInventory")
//===================================================================================
// CustomItem events.
// When an event trigger fires these values allow
// the action code to determine which event was dispatched.
// The functions listed below can be used to get information about the event.
//
// The on destroy event only lets you get the handle id,
// but no longer the handle itself.
//===================================================================================
globals
//=========================================
// Available Event Triggers.
//=========================================
constant trigger EVENT_CUSTOM_ITEM_CREATE = CreateTrigger()
constant trigger EVENT_CUSTOM_ITEM_DESTROY = CreateTrigger()// Does not allow .getItem()
endglobals
globals
//=======================================================
// Event Variables
//=======================================================
// This variable updates before an event fires.
// Get the information through the function below.
private integer eventIndex = 0
endglobals
//=======================================================
// Trigger Inventory Event API.
//=======================================================
// The function only return its proper information when called
// from within a registered trigger action function.
// For incorrect usage they will return "0".
// Returns the triggering CustomItem instance.
constant function GetTriggerCustomItem takes nothing returns CustomItem
return eventIndex
endfunction
// Registers code to available CustomItem events.
// Condition function not have to return a boolean, as the function outsmarts PJASS.
function RegisterCustomItemEvent takes trigger trig, code func returns nothing
if (trig == EVENT_CUSTOM_ITEM_CREATE) or (trig == EVENT_CUSTOM_ITEM_DESTROY) then
call TriggerAddCondition(trig, Condition(func))
endif
endfunction
//=======================================================
// Event Trigger Evaluation.
//=======================================================
// Fires CustomItem events while providing recursion safety.
private function FireEvent takes trigger trig, integer index returns nothing
local integer prevIndex = eventIndex
set eventIndex = index
call TriggerEvaluate(trig)
set eventIndex = prevIndex
endfunction
//===================================================================================
// ItemClass Code.
//===================================================================================
//! runtextmacro CUSTOM_ITEM_CLASS_CODE()
//===================================================================================
// ItemCosts Code. ( Optional )
//===================================================================================
//! runtextmacro CUSTOM_ITEM_COST_CODE("LIBRARY_GetItemCost")
//===================================================================================
// CustomItem API. Extends object editor fields.
//===================================================================================
// I reserved 10 parent keys inside the Inventory_Table for custom item data fields.
// INVENTORY_KEY_ITEM_DATA to INVENTORY_KEY_ITEM_DATA + 10.
//==========================
// Item Id Registration.
//==========================
// Items of type ITEM_TYPE_POWERUP are not allowed.
// Registers an item id as CustomItem.
function RegisterCustomItemId takes integer itemId, ItemClass class returns nothing
debug local boolean has = HaveSavedInteger(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA, itemId)
debug call ThrowError((itemId == 0), "CustomItem", "RegisterCustomItemId", "itemId", 0, "Invalid item id ( 0 )!")
debug call ThrowError(IsItemIdPowerup(itemId), "CustomItem", "RegisterCustomItemId", "itemId", 0, GetObjectName(itemId) + " has an invalid item type for CustomItems ( ITEM_TYPE_POWERUP )!")
debug call ThrowError(has, "CustomItem", "RegisterCustomItemId", "itemId", 0, "Attempt to register " + GetObjectName(itemId) + " twice!")
//
call SaveInteger(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA, itemId, class)
endfunction
// Returns true for registered item ids.
function IsCustomItemId takes integer itemId returns boolean
return HaveSavedInteger(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA, itemId)
endfunction
function IsCustomItem takes item whichItem returns boolean
return IsCustomItemId(GetItemTypeId(whichItem))
endfunction
//==========================
// Item Class Related.
//==========================
// Returns the item class for an item id.
// Returns "0" if the item is not registered.
function GetCustomItemIdClass takes integer itemId returns ItemClass
return LoadInteger(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA, itemId)
endfunction
function GetCustomItemClass takes item whichItem returns ItemClass
return GetCustomItemIdClass(GetItemTypeId(whichItem))
endfunction
// Returns true if a given item is of ItemClass "class".
function IsCustomItemClass takes item whichItem, ItemClass class returns boolean
return GetCustomItemIdClass(GetItemTypeId(whichItem)) == class
endfunction
//===================================================================================
// Icon path and DISpath, twohanded.
//===================================================================================
function GetCustomItemIconPath takes integer itemId returns string
return LoadStr(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA + 1, itemId)
endfunction
function GetCustomItemIconDISPath takes integer itemId returns string
return LoadStr(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA + 1, -itemId)
endfunction
function GetCustomItemIconId takes integer itemId returns integer
return LoadInteger(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA + 1, itemId)
endfunction
function GetCustomItemIconDISId takes integer itemId returns integer
return LoadInteger(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA + 1, -itemId)
endfunction
// Set item icon path and DIS path.
function SetCustomItemIcon takes integer itemId, string filePath, string DISfilePath, integer destId, integer DISdestId returns nothing
call SaveInteger(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA + 1, itemId, destId)
call SaveInteger(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA + 1, -itemId, DISdestId)
call SaveStr(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA + 1, itemId, filePath)
call SaveStr(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA + 1, -itemId, DISfilePath)
endfunction
// Set item twohanded.
function IsCustomItemTwohand takes item whichItem returns boolean
return LoadBoolean(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA + 1, GetItemTypeId(whichItem))
endfunction
function SetCustomItemTwohand takes integer itemId, boolean flag returns nothing
call SaveBoolean(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA + 1, itemId, flag)
endfunction
//===================================================================================
// Effects via string.
//===================================================================================
// Item based fxs.
function GetCustomItemFxFilePath takes integer itemId returns string
return LoadStr(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA + 2, itemId)
endfunction
function GetCustomItemFxPos takes integer itemId returns string
return LoadStr(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA + 2, -itemId)
endfunction
function SetCustomItemFxFilePath takes integer itemId, string path, string pos returns nothing
call SaveStr(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA + 2, itemId, path)
call SaveStr(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA + 2, -itemId, pos)
endfunction
//===================================================================================
// Animation properties, Effects displayed via ability.
//===================================================================================
function GetCustomItemAnimationProperty takes integer itemId returns string
return LoadStr(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA + 3, itemId)
endfunction
function SetCustomItemAnimationProperty takes integer itemId, string tag returns nothing
call SaveStr(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA + 3, itemId, tag)
endfunction
// Complex fx.
function SetCustomItemFxAbilityId takes integer itemId, integer abilityId returns nothing
call SaveInteger(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA + 3, itemId, abilityId)
endfunction
function GetCustomItemFxAbilityId takes integer itemId returns integer
return LoadInteger(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA + 3, itemId)
endfunction
//===================================================================================
// Socket count from min to max. Min == 0, Max == 6
//===================================================================================
function SetCustomItemIdSockets takes integer itemId, integer min, integer max returns nothing
call SaveInteger(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA + 3, -itemId, min)
call SaveInteger(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA + 3, itemId, max)
endfunction
function GetCustomItemIdSockets takes integer itemId returns integer
local integer min = LoadInteger(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA + 3, -itemId)
local integer max = LoadInteger(Inventory_GetTable(), INVENTORY_KEY_ITEM_DATA + 3, itemId)
return GetRandomInt(min, max)
endfunction
//===========================
// Useful Wrapper Functions.
//===========================
function IsItemStackableWithItem takes item a, item b returns boolean
return GetItemCharges(a) > 0 and GetItemCharges(b) > 0 and GetItemTypeId(a) == GetItemTypeId(b)
endfunction
function IsItemPurchasable takes item whichItem returns boolean
return (GetItemType(whichItem) == ITEM_TYPE_PURCHASABLE)
endfunction
function IsItemCustomNativeCompatible takes item whichItem returns boolean
return GetItemType(whichItem) == ITEM_TYPE_CHARGED or GetItemType(whichItem) == ITEM_TYPE_PURCHASABLE
endfunction
function StackChargedItems takes item a, item b returns boolean
local integer chargesA
local integer chargesB
local integer max
if IsItemStackableWithItem(a, b) then
set chargesA = GetItemCharges(a)
set chargesB = GetItemCharges(b)
set max = IMinBJ(chargesB, IMaxBJ(0, 99 - chargesA))
call SetItemCharges(a, chargesA + max)
if (max < chargesB) then
call SetItemCharges(b, chargesB - max)
else
call RemoveItem(b)
endif
return true
endif
return false
endfunction
//===========================
// Private Stuff.
//===========================
private function SetItemUnavailable takes item i returns nothing
call DisableTrigger(GetPlayerUnitEventTrigger(EVENT_PLAYER_UNIT_DROP_ITEM))
call SetItemPosition(i, WorldBounds.minX, WorldBounds.minY)
call EnableTrigger(GetPlayerUnitEventTrigger(EVENT_PLAYER_UNIT_DROP_ITEM))
call SetItemVisible(i, false)
endfunction
private function AddPlayerResource takes player p, integer gold, integer lumber returns nothing
call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD) + gold)
call SetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER, GetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER) + lumber)
endfunction
//====================================
// Wrappers methods .....
//====================================
private module CustomItemExtendedAPI
// Converts items to custom items on the first interaction.
static method operator [] takes item i returns thistype
local thistype this = GetHandleId(i)
if not (exists) then
return create(i)
endif
return this
endmethod
method getOwner takes nothing returns unit
return LoadUnitHandle(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM_OWNER, this)
endmethod
method setOwner takes unit source returns nothing
call RemoveSavedHandle(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM_OWNER, this)
call SaveUnitHandle(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM_OWNER, this, source)
endmethod
// Not implemented. ( Item cooldowns )
method isReady takes nothing returns boolean
return true
endmethod
// Returns true for a total item merging.
method mergeCharges takes item add returns boolean
return StackChargedItems(getItem(), add)
endmethod
method getClass takes nothing returns ItemClass
return GetCustomItemClass(getItem())
endmethod
method getQuality takes nothing returns real
return LoadReal(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM, this)
endmethod
method setQuality takes real value returns nothing
call SaveReal(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM, this, value)
endmethod
method getWoodCost takes nothing returns integer
return R2I(GetItemWoodCost(getItem())*INVENTORY_SELL_ITEM_RETURN_RATE*getQuality())
endmethod
method getGoldCost takes nothing returns integer
return R2I(GetItemGoldCost(getItem())*INVENTORY_SELL_ITEM_RETURN_RATE*getQuality())
endmethod
method setName takes string name returns nothing
call SaveStr(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM, this, name)
endmethod
method getName takes nothing returns string
if HaveSavedString(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM, this) then
return LoadStr(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM, this)
endif
return GetItemName(getItem())
endmethod
method isItemPawnPossible takes unit seller, unit shop returns boolean
return (seller == getOwner()) and (seller != shop) and (seller != null) and IsItemPawnable(getItem())
endmethod
method pawnItem takes unit shop returns nothing
call AddPlayerResource(GetOwningPlayer(shop), getGoldCost(), getWoodCost())
endmethod
method isItemBuyPossible takes unit buyer, unit seller returns boolean
local player user = GetOwningPlayer(buyer)
local boolean gold = GetPlayerState(user, PLAYER_STATE_RESOURCE_GOLD) >= GetItemGoldCost(getItem())
local boolean wood = GetPlayerState(user, PLAYER_STATE_RESOURCE_LUMBER) >= GetItemWoodCost(getItem())
set user = null
return (seller != getOwner()) and wood and gold and IsItemSellable(getItem())
endmethod
endmodule
//===================================================
// Core.
//===================================================
struct CustomItem extends array
method operator exists takes nothing returns boolean
return HaveSavedHandle(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM, this)
endmethod
method getItem takes nothing returns item
return LoadItemHandle(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM, this)
endmethod
method getHandle takes nothing returns item
debug call BJDebugMsg("getHandle should not be used")
return getItem()
endmethod
method getItemId takes nothing returns integer
return GetItemTypeId(getItem())
endmethod
method getCell takes nothing returns integer
return LoadInteger(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM_CELL, this)
endmethod
method setCell takes integer id, boolean disable returns nothing
call SaveInteger(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM_CELL, this, id)
call SaveBoolean(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM_CELL, this, disable)
if (disable) then
call SetItemUnavailable(getItem())
endif
endmethod
method placeInMap takes real x, real y returns nothing
if HaveSavedBoolean(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM_CELL, this) then
call RemoveSavedHandle(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM_OWNER, this)
call RemoveSavedBoolean(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM_CELL, this)
call RemoveSavedInteger(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM_CELL, this)
call SetItemPosition(getItem(), x, y)
//
debug else
debug call ThrowWarning(true, "CustomItem", "placeInMap", "protected", this, GetItemName(getItem()) + " was not protected!")
endif
endmethod
method isCustom takes nothing returns boolean
return HaveSavedBoolean(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM, this)
endmethod
// Removes all hashtable entries for this item.
private method clear takes nothing returns nothing
call RemoveSavedString(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM, this)
call RemoveSavedBoolean(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM, this)
call RemoveSavedHandle(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM, this)
call RemoveSavedHandle(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM_OWNER, this)
call RemoveSavedInteger(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM_OWNER, this)
call RemoveSavedInteger(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM_CELL, this)
endmethod
// Read-ability. next/prev...
private method operator next takes nothing returns thistype
return LoadInteger(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM_NEXT, this)
endmethod
private method operator prev takes nothing returns thistype
return LoadInteger(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM_PREV, this)
endmethod
private method remove takes nothing returns nothing
call SaveInteger(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM_NEXT, prev, next)
call SaveInteger(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM_PREV, next, prev)
endmethod
private method add takes nothing returns nothing
call SaveInteger(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM_NEXT, this, 0)
call SaveInteger(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM_PREV, this, thistype(0).prev)
call SaveInteger(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM_NEXT, thistype(0).prev, this)
call SaveInteger(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM_PREV, 0, this)
endmethod
private method destroy takes nothing returns nothing
if (exists) then
call remove()
if isCustom() then
call FireEvent(EVENT_CUSTOM_ITEM_DESTROY, this)
endif
call clear()
//
debug else
debug call ThrowWarning(not exists, "CustomItem", "deallocate", "handle", this, "Invalid deallocation process!")
endif
endmethod
private static thistype pos
private static method checkList takes nothing returns nothing
local thistype this = thistype.pos
local integer max = 1000// I don't know when we would hit the OP-limit.
loop
exitwhen (0 == this)
exitwhen (0 == max)
if (getItemId() == 0) then
call destroy()
endif
set this = next
set max = max - 1
endloop
if (this != 0) then
set thistype.pos = this
call ForForce(bj_FORCE_PLAYER[0], function thistype.checkList)
endif
endmethod
private static integer wasted = 0
static method create takes item i returns thistype
local thistype this = GetHandleId(i)
local integer itemId = GetItemTypeId(i)
// Invalid.
if (0 != itemId) and not IsItemPowerup(i) then
// Cleanup what is already there.
set wasted = wasted + 1
if (wasted >= 16) then
set wasted = 0
set thistype.pos = thistype(0).next// First node.
call ForForce(bj_FORCE_PLAYER[0], function thistype.checkList)
endif
// Add to list.
call add()
// Reference.
call SaveItemHandle(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM, this, i)
call SaveReal(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM, this, 1.)// Quality.
// Fire event.
if (IsCustomItemId(itemId)) then
call SaveBoolean(Inventory_GetTable(), INVENTORY_KEY_CUSTOM_ITEM, this, true)
call FireEvent(EVENT_CUSTOM_ITEM_CREATE, this)
endif
endif
//
return this
endmethod
implement CustomItemExtendedAPI
endstruct
function GetCustomItem takes item i returns CustomItem
local CustomItem id = GetHandleId(i)
if id.exists then
return id
endif
return CustomItem.create(i)
endfunction
//===================================================
// Item classes.
//===================================================
// Items of a specific class can only go to a slot of that class
// or to a slot of a parent class of that specific class.
// Example: ANY > Hand > Sword.
// --> A "Sword" class item can only go into slot declared as "Sword".
// --> A "Hand" class item only into "Hand" and "Sword"
// --> An "ANY" class item can go into all three slots.
//! textmacro CUSTOM_ITEM_CLASS_CODE
struct ItemClass extends array
readonly thistype parent
readonly string name
// Every item class is based of ANY.
static constant integer ANY = 1
private static method init takes nothing returns nothing
set thistype(ANY).name = "Any Class"
set thistype(ANY).parent = thistype.ANY
endmethod
implement UIInit// Runs on map init. Calls thistype.init.
private static integer alloc = thistype.ANY
private static method allocate takes string text returns thistype
local thistype this = alloc + 1
set alloc = this
set name = text
return this
endmethod
method operator root takes nothing returns boolean
return (thistype.ANY == integer(this))
endmethod
// Every class is a sub class of ItemClass.ANY.
static method create takes string name returns thistype
local thistype this = allocate(name)
set parent = thistype.ANY
return this
endmethod
// Any -> Hand -> Weapon -> Sword -> Mighty Sword ....
method createSubClass takes string name returns thistype
local thistype node = allocate(name)
set node.parent = this
return node
endmethod
endstruct
//! endtextmacro
//! textmacro CUSTOM_ITEM_COST_CODE takes BOOL
// I was not going for performance, but accurarity.
static if not $BOOL$ then
globals
private integer itemId = 0
private unit shop = null
endglobals
private function MarkOtherItems takes nothing returns nothing
call SaveBoolean(Inventory_GetTable(), INVENTORY_ITEM_COSTS, GetHandleId(GetEnumItem()), true)
endfunction
private function EnumItems takes nothing returns nothing
local item i = GetEnumItem()
if itemId == GetItemTypeId(i) and not HaveSavedBoolean(Inventory_GetTable(), INVENTORY_ITEM_COSTS, GetHandleId(i)) then
call SaveInteger(Inventory_GetTable(), INVENTORY_ITEM_CHARGES, itemId, GetItemCharges(i))
call RemoveItem(i)
set itemId = 0
endif
set i = null
endfunction
private function LogItem takes integer id returns nothing
local real shopX = GetUnitX(shop)
local real shopY = GetUnitY(shop)
// Pepare gold and lumber.
local integer gold = GetPlayerState(UI_NEUTRAL_PLAYER, PLAYER_STATE_RESOURCE_GOLD)
local integer wood = GetPlayerState(UI_NEUTRAL_PLAYER, PLAYER_STATE_RESOURCE_LUMBER)
call SetPlayerState(UI_NEUTRAL_PLAYER, PLAYER_STATE_RESOURCE_GOLD, 1000000)
call SetPlayerState(UI_NEUTRAL_PLAYER, PLAYER_STATE_RESOURCE_LUMBER, 1000000)
// Find other close items.
call SetRect(UI_RECT, shopX - 1088, shopY - 1088, shopX + 1088, shopY + 1088)
call EnumItemsInRect(UI_RECT, null, function MarkOtherItems)
// Prepare the shop and order it to buy the item.
call UnitAddAbility(shop, 'Asid')
call AddItemToStock(shop, id, 1, 1)
call IssueNeutralImmediateOrderById(UI_NEUTRAL_PLAYER, shop, id)
call RemoveItemFromStock(shop, id)
call UnitRemoveAbility(shop, 'Asid')
// Find the sold item. UI_Rect is a global shared rect
// and the buy order fires trigger events.
// Therefore I set the rect again.
set itemId = id
call SetRect(UI_RECT, shopX - 1088, shopY - 1088, shopX + 1088, shopY + 1088)
call EnumItemsInRect(UI_RECT, null, function EnumItems)
call FlushChildHashtable(Inventory_GetTable(), INVENTORY_ITEM_COSTS)
// Save data for this item and restore the player state.
call SaveInteger(Inventory_GetTable(), INVENTORY_ITEM_GOLD_COST, id, 1000000 - GetPlayerState(UI_NEUTRAL_PLAYER, PLAYER_STATE_RESOURCE_GOLD))
call SaveInteger(Inventory_GetTable(), INVENTORY_ITEM_WOOD_COST, id, 1000000 - GetPlayerState(UI_NEUTRAL_PLAYER, PLAYER_STATE_RESOURCE_LUMBER))
call SetPlayerState(UI_NEUTRAL_PLAYER, PLAYER_STATE_RESOURCE_GOLD, gold)
call SetPlayerState(UI_NEUTRAL_PLAYER, PLAYER_STATE_RESOURCE_LUMBER, wood)
endfunction
function GetItemTypeIdGoldCost takes integer id returns integer
if not HaveSavedInteger(Inventory_GetTable(), INVENTORY_ITEM_GOLD_COST, id) then
call LogItem(id)
endif
return LoadInteger(Inventory_GetTable(), INVENTORY_ITEM_GOLD_COST, id)
endfunction
function GetItemTypeIdWoodCost takes integer id returns integer
if not HaveSavedInteger(Inventory_GetTable(), INVENTORY_ITEM_WOOD_COST, id) then
call LogItem(id)
endif
return LoadInteger(Inventory_GetTable(), INVENTORY_ITEM_WOOD_COST, id)
endfunction
function GetItemTypeIdCharges takes integer id returns integer
if not HaveSavedInteger(Inventory_GetTable(), INVENTORY_ITEM_CHARGES, id) then
call LogItem(id)
endif
return LoadInteger(Inventory_GetTable(), INVENTORY_ITEM_CHARGES, id)
endfunction
function GetItemGoldCost takes item i returns integer
local integer id = GetItemTypeId(i)
local real s2 = GetItemTypeIdCharges(id)
if (s2 > 0) then
return R2I(LoadInteger(Inventory_GetTable(), INVENTORY_ITEM_GOLD_COST, id)*(GetItemCharges(i)/s2))
endif
return LoadInteger(Inventory_GetTable(), INVENTORY_ITEM_GOLD_COST, id)
endfunction
function GetItemWoodCost takes item i returns integer
local integer id = GetItemTypeId(i)
local real s2 = GetItemTypeIdCharges(id)
if (s2 > 0) then
return R2I(LoadInteger(Inventory_GetTable(), INVENTORY_ITEM_WOOD_COST, id)*(GetItemCharges(i)/s2))
endif
return LoadInteger(Inventory_GetTable(), INVENTORY_ITEM_WOOD_COST, id)
endfunction
private struct I extends array
private static method init takes nothing returns nothing
local boolean prev = ToogleUnitIndexer(false)
set shop = CreateUnit(UI_NEUTRAL_PLAYER, 'hpea', 0, 0, 0)
call ToogleUnitIndexer(prev)
call ShowUnit(shop, false)
call UnitAddAbility(shop, 'Aloc')
endmethod
implement UIInit
endstruct
endif
//! endtextmacro
endlibrary
//TESH.scrollpos=160
//TESH.alwaysfold=0
library CustomItemAffix uses ErrorMessage, InventoryCore
//===================================================================================
// CustomItemAffix rolls random affixes for custom items.
//===================================================================================
// Read-ability. ( in item parser )
function ItemHasRandomAffixes takes integer itemId returns boolean
return HaveSavedBoolean(Inventory_GetTable(), INVENTORY_RANDOM_AFFIX_TYPES, itemId)
endfunction
function ItemHasConstantAffixes takes integer itemId returns boolean
return HaveSavedBoolean(Inventory_GetTable(), INVENTORY_CONSTANT_AFFIX_TYPES, itemId)
endfunction
function ItemRollsAffixes takes integer itemId returns boolean
return ItemHasRandomAffixes(itemId) or ItemHasConstantAffixes(itemId)
endfunction
private function SearchNodeMatch takes integer parent, integer list, integer match returns integer
local integer node = Inventory_GetListFirstNode(list)
loop
exitwhen 0 == node
if LoadInteger(Inventory_GetTable(), parent, node) == match then
return node
endif
set node = Inventory_GetListNextNode(node)
endloop
return -1
endfunction
// Start of with 0.
function SetItemIdAffixRollRange takes integer itemId, integer depth, integer minimum, integer maximum returns nothing
local integer list = Inventory_GetListByKey(INVENTORY_AFFIX_DEPTH_LIST, itemId)
local integer node = SearchNodeMatch(INVENTORY_AFFIX_DEPTH_DEPTH, list, depth)
if (node == -1) then
set node = Inventory_EnqueueToList(list)
endif
call SaveInteger(Inventory_GetTable(), INVENTORY_AFFIX_DEPTH_DEPTH, node, depth)
call SaveInteger(Inventory_GetTable(), INVENTORY_AFFIX_DEPTH_RANDOM, -node, minimum)
call SaveInteger(Inventory_GetTable(), INVENTORY_AFFIX_DEPTH_RANDOM, node, IMaxBJ(minimum, maximum))
endfunction
function AddItemIdConstantAffix takes integer itemId, string affixType, integer affixId, real min, real max returns nothing
local integer list = Inventory_GetListByKey(INVENTORY_CONSTANT_AFFIX_LIST, itemId)
local integer node = Inventory_EnqueueToList(list)
call SaveStr(Inventory_GetTable(), INVENTORY_CONSTANT_AFFIX_TYPES, node, affixType) // Identifier
call SaveReal(Inventory_GetTable(), INVENTORY_CONSTANT_AFFIX_TYPES, -node, min) // Min
call SaveReal(Inventory_GetTable(), INVENTORY_CONSTANT_AFFIX_TYPES, node, RMaxBJ(min, max)) // Max
call SaveInteger(Inventory_GetTable(), INVENTORY_CONSTANT_AFFIX_TYPES, node, affixId) // Id
call SaveBoolean(Inventory_GetTable(), INVENTORY_CONSTANT_AFFIX_TYPES, itemId, true) // System knows item id.
endfunction
function AddItemIdRandomAffix takes integer itemId, integer depth, string affixType, integer affixId, real min, real max returns nothing
local integer list = Inventory_GetListByKey(INVENTORY_RANDOM_AFFIX_LIST, itemId)
local integer node = Inventory_EnqueueToList(list)
call SaveStr(Inventory_GetTable(), INVENTORY_RANDOM_AFFIX_TYPES, node, affixType)
call SaveReal(Inventory_GetTable(), INVENTORY_RANDOM_AFFIX_TYPES, -node, min)
call SaveReal(Inventory_GetTable(), INVENTORY_RANDOM_AFFIX_TYPES, node, RMaxBJ(min, max))
call SaveInteger(Inventory_GetTable(), INVENTORY_RANDOM_AFFIX_TYPES, -node, depth) // Loop index.
call SaveInteger(Inventory_GetTable(), INVENTORY_RANDOM_AFFIX_TYPES, node, affixId)
call SaveBoolean(Inventory_GetTable(), INVENTORY_RANDOM_AFFIX_TYPES, itemId, true)
endfunction
//===================================================================================
// Internal API
//===================================================================================
globals
private string affixType = null
private real quality = 0
private real rolled = 0
private integer affixId = 0
endglobals
function PrepareRandomAffixRoll takes integer itemId, integer depth returns nothing
local integer list = Inventory_GetListByKey(INVENTORY_RANDOM_AFFIX_LIST, itemId)
local integer node = Inventory_GetListFirstNode(list)
local integer size = 0
call Inventory_FlushChild(INVENTORY_AFFIX_STACK)
loop
exitwhen node == 0
if LoadInteger(Inventory_GetTable(), INVENTORY_RANDOM_AFFIX_TYPES, -node) == depth then
call SaveInteger(Inventory_GetTable(), INVENTORY_AFFIX_STACK, size, node)
set size = size + 1
endif
set node = Inventory_GetListNextNode(node)
endloop
call SaveInteger(Inventory_GetTable(), INVENTORY_AFFIX_STACK, -1, list)
call SaveInteger(Inventory_GetTable(), INVENTORY_AFFIX_STACK, -2, size)
endfunction
function PrepareConstantAffixRoll takes integer itemId returns nothing
local integer list = Inventory_GetListByKey(INVENTORY_CONSTANT_AFFIX_LIST, itemId)
local integer node = Inventory_GetListFirstNode(list)
local integer size = 0
call Inventory_FlushChild(INVENTORY_AFFIX_STACK)
loop
exitwhen node == 0
call SaveInteger(Inventory_GetTable(), INVENTORY_AFFIX_STACK, size, node)
set size = size + 1
set node = Inventory_GetListNextNode(node)
endloop
call SaveInteger(Inventory_GetTable(), INVENTORY_AFFIX_STACK, -1, list)
call SaveInteger(Inventory_GetTable(), INVENTORY_AFFIX_STACK, -2, size)
endfunction
private function Roll takes integer parent returns boolean
local integer list = LoadInteger(Inventory_GetTable(), INVENTORY_AFFIX_STACK, -1)
local integer size = LoadInteger(Inventory_GetTable(), INVENTORY_AFFIX_STACK, -2) - 1
local integer node
local real min
local real max
local integer roll
local integer trunc
if (size < 0) then
return false
endif
if parent == INVENTORY_RANDOM_AFFIX_TYPES then
set roll = GetRandomInt(0, size)
else
set roll = 0
endif
set node = LoadInteger(Inventory_GetTable(), INVENTORY_AFFIX_STACK, roll)
set min = LoadReal(Inventory_GetTable(), parent, -node)
set max = LoadReal(Inventory_GetTable(), parent, node)
set trunc = R2I(GetRandomReal(min, max) + .5)
set affixId = LoadInteger(Inventory_GetTable(), parent, node)
set affixType = LoadStr(Inventory_GetTable(), parent, node)
set rolled = trunc
if (max != 0) then
set quality = rolled/max
else
set quality = 1
endif
call SaveInteger(Inventory_GetTable(), INVENTORY_AFFIX_STACK, roll, LoadInteger(Inventory_GetTable(), INVENTORY_AFFIX_STACK, size))
call SaveInteger(Inventory_GetTable(), INVENTORY_AFFIX_STACK, -2, size)
return true
endfunction
function RollRandomAffix takes nothing returns boolean
return Roll(INVENTORY_RANDOM_AFFIX_TYPES)
endfunction
function RollConstantAffix takes nothing returns boolean
return Roll(INVENTORY_CONSTANT_AFFIX_TYPES)
endfunction
function AffixTableHasLoopDepth takes integer itemId, integer depth returns boolean
local integer list = Inventory_GetListByKey(INVENTORY_AFFIX_DEPTH_LIST, itemId)
return (SearchNodeMatch(INVENTORY_AFFIX_DEPTH_DEPTH, list, depth) != -1)
endfunction
function GetAffixMaxCount takes integer itemId, integer depth returns integer
local integer list = Inventory_GetListByKey(INVENTORY_AFFIX_DEPTH_LIST, itemId)
local integer node = SearchNodeMatch(INVENTORY_AFFIX_DEPTH_DEPTH, list, depth)
return LoadInteger(Inventory_GetTable(), INVENTORY_AFFIX_DEPTH_RANDOM, node)
endfunction
private function GetAffixMinCount takes integer itemId, integer depth returns integer
local integer list = Inventory_GetListByKey(INVENTORY_AFFIX_DEPTH_LIST, itemId)
local integer node = SearchNodeMatch(INVENTORY_AFFIX_DEPTH_DEPTH, list, depth)
return LoadInteger(Inventory_GetTable(), INVENTORY_AFFIX_DEPTH_RANDOM, -node)
endfunction
function RollAffixCount takes integer itemId, integer depth returns integer
return GetRandomInt(GetAffixMinCount(itemId, depth), GetAffixMaxCount(itemId, depth))
endfunction
constant function GetRolledAffixId takes nothing returns integer
return affixId
endfunction
constant function GetRolledAffixType takes nothing returns string
return affixType
endfunction
constant function GetRolledAffixAmount takes nothing returns real
return rolled
endfunction
constant function GetRolledAffixQuality takes nothing returns real
return quality
endfunction
endlibrary
//TESH.scrollpos=18
//TESH.alwaysfold=0
library CustomItemName initializer Init uses InventoryCore
//===================================================================================
// CustomItemName is able to generate
// random names for custom items, if
// the following conditions match:
// 1.) The item must be of type ITEM_TYPE_PERMANENT.
// 2.) The item must roll random affixes.
//===================================================================================
// Factors which influence the naming are:
// 1.) Item quality. ( In range of 0% - 100% )
// 2.) Strongest rolled affix.
//===================================================================================
// An item name is composed of
// prefix + GetItemName(item) + suffix.
// You can define all suffixes and prefixes in
// the Inventory User Setup.
//
// In case the strongest rolled affix is
// an ability id, the item name will become:
// GetPrefix(abilityId) + GetItemName(item) + of + GetObjectName(abilityId)
//===================================================================================
private function SetPrefix takes string prefix, integer affix, integer min, integer max returns nothing
set affix = affix*101// I wonder if we can hit a limit sequence here. abilityId*101
// Grrr
set min = IMaxBJ(0, min)
loop
exitwhen (min > max)
exitwhen (min > 100)
call SaveStr(Inventory_GetTable(), INVENTORY_ITEM_NAME, affix + min, prefix)
set min = min + 1
endloop
endfunction
private function SetSuffix takes string suffix, integer affix, integer min, integer max returns nothing
set affix = affix*101
set min = IMaxBJ(0, min)
loop
exitwhen (min > max)
exitwhen (min > 100)
call SaveStr(Inventory_GetTable(), INVENTORY_ITEM_NAME, -affix - min, suffix)
set min = min + 1
endloop
endfunction
//===================================================================================
// Init prefixes and suffixes.
//===================================================================================
private function Init takes nothing returns nothing
//! runtextmacro ITEM_PARSER_RANDOM_ITEM_NAME()
endfunction
//===================================================================================
// Naming. Called from the item parser.
//===================================================================================
// For read-ability.
private function GetSuffix takes integer affix, integer quality returns string
return LoadStr(Inventory_GetTable(), INVENTORY_ITEM_NAME, -(affix*101) - quality)
endfunction
private function GetPrefix takes integer affix, integer quality returns string
return LoadStr(Inventory_GetTable(), INVENTORY_ITEM_NAME, affix*101 + quality)
endfunction
function GenerateCustomItemName takes item whichItem, integer affix, real quality returns string
local integer value = R2I(quality + .5)
if value > 100 then
set value = 100
elseif value < 0 then
set value = 0
endif
if (GetItemType(whichItem) != ITEM_TYPE_PERMANENT) then
return GetItemName(whichItem)
// Probably an ability id.
elseif affix > 8910 then
return GetPrefix(affix, 0) + GetItemName(whichItem) + " of " + GetObjectName(affix)
endif
// Bonus.
return GetPrefix(affix, value) + " " + GetItemName(whichItem) + " " + GetSuffix(affix, value)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library CustomItemParser initializer InitSuffixes uses CustomItem, InventoryUserSetup, CustomItemName, CustomItemAffix, CustomItemAffixList
//===================================================================================
// This library parses each new created CustomItem
// to create the bonus and ability lists.
// Furthermore it prepares the final item tooltip.
//===================================================================================
//===================================================================================
// Bonus tooltip suffixes.
//===================================================================================
// Listed for read-ability.
// Define a suffix for an affixId.
private function SetSuffix takes integer affixId, string text returns nothing
call SaveStr(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER_SUFFIX, affixId, text)
endfunction
private function GetSuffix takes integer affixId returns string
return LoadStr(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER_SUFFIX, affixId)
endfunction
// Init you affix suffixes here.
private function InitSuffixes takes nothing returns nothing
//! runtextmacro optional ITEM_PARSER_ITEM_TOOLTIP_SUFFIXES("INVENTORY_ENABLE_DYNAMIC_ITEMS")
endfunction
//===================================================================================
// Writing the item tooltip.
//===================================================================================
// Imports all fragments defined for that specific item id.
private function ImportConstantTooltipFragments takes CustomItem id returns nothing
local integer itemId = id.getItemId()
local integer dex = 0
local integer end = GetItemTooltipSize(itemId)
local integer pos
loop
exitwhen (dex == end)
set pos = GetItemTooltipFragmentPointer(itemId, dex)
call SetItemTooltipFragment(id, GetItemTooltipPointerSize(id, pos), GetItemTooltipFragment(itemId, dex), GetItemTooltipFragmentIcon(itemId, dex), pos)
set dex = dex + 1
endloop
call GenerateItemTooltip(id)
endfunction
// Imports all fragments from the ability and bonus list of an item handle.
// Ability list child key: -id
// Bonus list child key: id
private function ImportDynamicTooltipFragments takes CustomItem id returns nothing
local string prefix = "|cff99b4d1+"
local string icon = "UI\\Minimap\\MinimapIconCreepLoc2.blp"
local string text = null
// Access the bonus list.
local integer size = GetItemTooltipPointerSize(id, TOOLTIP_POINTER_BONUS_LIST)
local integer list = Inventory_GetListByKey(INVENTORY_ITEM_ID_BONUS_LIST, id)
local integer node = Inventory_GetListFirstNode(list)
local integer affix
local integer amount
loop
exitwhen node == 0
set affix = LoadInteger(Inventory_GetTable(), INVENTORY_ITEM_ID_AFFIXES, node)
set amount = LoadInteger(Inventory_GetTable(), INVENTORY_ITEM_ID_AFFIXES, -node)
set text = prefix + I2S(amount) + "|r " + GetSuffix(affix)
call SetItemTooltipFragment(id, size, text, icon, TOOLTIP_POINTER_BONUS_LIST)
set size = size + 1
set node = Inventory_GetListNextNode(node)
endloop
// Access the ability list.
set size = GetItemTooltipPointerSize(id, TOOLTIP_POINTER_ABILITY_LIST)
set list = Inventory_GetListByKey(INVENTORY_ITEM_ID_ABILITY_LIST, id)
set node = Inventory_GetListFirstNode(list)
loop
exitwhen node == 0
set affix = LoadInteger(Inventory_GetTable(), INVENTORY_ITEM_ID_AFFIXES, node)
set amount = LoadInteger(Inventory_GetTable(), INVENTORY_ITEM_ID_AFFIXES, -node)
set text = prefix + I2S(amount) + " to|r " + GetObjectName(affix)
call SetItemTooltipFragment(id, size, text, icon, TOOLTIP_POINTER_ABILITY_LIST)
set size = size + 1
set node = Inventory_GetListNextNode(node)
endloop
endfunction
function UpdateCustomItemTooltip takes item whichItem returns nothing
local CustomItem id = CustomItem[whichItem]
if not id.exists then
return
endif
// Clear what was written before.
call FlushItemTooltip(id)
// Get what should always be there.
call ImportConstantTooltipFragments(id)
// Get what is different per handle id.
call ImportDynamicTooltipFragments(id)
// Write it down.
call GenerateItemTooltip(id)
endfunction
//===================================================================================
// Dynamic item generation.
// This code block is responsible for
// items with random rolled affixes.
//===================================================================================
// Child key offsets used:
// 0x186A0 - Rolled affix quality, compared to the maximum possible rolled value.
// 0xF4240 - In PrepareParser, getters and setters below.
//
// Item quality is combined of:
// 1.) rolled value/maximum possible value
// 2.) rolled affix count/maximum possible affix count.
private function PrepareParser takes item forItem returns nothing
call Inventory_FlushChild(INVENTORY_KEY_ITEM_PARSER)
//
call SaveItemHandle(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER, 0xF4240, forItem)// Item to parse.
call SaveReal(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER, 0xF4240, 1.) // Item quality.
call SaveInteger(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER, 0xF4240, 0) // Stack size.
call SaveInteger(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER, -0xF4240, 1) // Item quality divisor.
endfunction
// For read-ability only.
private function SetStackSize takes integer size returns nothing
call SaveInteger(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER, 0xF4240, size)
endfunction
private function GetStackSize takes nothing returns integer
return LoadInteger(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER, 0xF4240)
endfunction
private function SetQualityDivisor takes integer new returns nothing
call SaveInteger(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER, -0xF4240, new)
endfunction
private function GetQualityDivisor takes nothing returns integer
return LoadInteger(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER, -0xF4240)
endfunction
private function SetQuality takes real new returns nothing
call SaveReal(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER, 0xF4240, new)
endfunction
private function GetQuality takes nothing returns real
return LoadReal(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER, 0xF4240)
endfunction
private function GetItem takes nothing returns item
return LoadItemHandle(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER, 0xF4240)
endfunction
// By default the item quality is 1.
private function MeasureQuality takes nothing returns nothing
local CustomItem id = GetHandleId(GetItem())
local integer affixType = StringHash(LoadStr(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER, 0x186A0))
local integer affixId = LoadInteger(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER, 0x186A0)
local real quality = LoadReal(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER, 0x186A0)
//
if (affixId != 0) then
set quality = (quality + GetQuality()/GetQualityDivisor())/2
//
call id.setQuality(quality)
call id.setName(GenerateCustomItemName(id.getItem(), affixId, quality))
endif
endfunction
// Registers what was rolled.
private function RegisterAffixes takes nothing returns nothing
local item temp = GetItem()
//
debug local string error = "[" + GetItemName(temp) + "] Missing requirement: Can't register"
debug local string valid = "Valid affix string indentifiers are 'Bonus' or 'Ability'"
//
// Prepare type identifiers.
local integer bonus = StringHash("Bonus")
local integer skill = StringHash("Ability")
local integer dex = 0
local integer end = GetStackSize()
//
local integer affixType
local real affixAmount
local integer affixId
local integer trunc
//
// Start.
loop
exitwhen (dex >= end)
set affixType = StringHash(LoadStr(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER, dex))
set affixAmount = LoadReal(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER, dex)
set affixId = LoadInteger(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER, dex)
// Check for Bonuses.
if (affixType == bonus) then
// Earth-Fury's BonusMod library.
static if LIBRARY_BonusMod then
set trunc = R2I(affixAmount)// Pretty solid.
if (trunc != 0) then
call AddCustomItemBonus(temp, affixId, trunc)
endif
//
// BonusMod is not present, but bonuses are registered by the user.
debug else
debug call ThrowWarning(true, "CustomItemParser", "RegisterAffixes", "BonusMod", 0, error + " bonuses without library BonusMod!")
endif
//
// Check for native abilities.
elseif (affixType == skill) then
set trunc = R2I(affixAmount)
if (trunc != 0) then
call AddCustomItemAbility(temp, affixId, trunc)
endif
//
// Invalid string identifier.
debug else
debug call ThrowWarning(true, "CustomItemParser", "RegisterAffixes", "affixType", 0, "Can't identify affix type! [" + LoadStr(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER, dex) + "]! \n" + valid)
endif
//
set dex = dex + 1
endloop
set temp = null
endfunction
private function SaveData takes nothing returns nothing
local integer stackSize = GetStackSize()
local real quality = GetRolledAffixQuality()
local real bestQuality = LoadReal(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER, 0x186A0)
//
call SaveInteger(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER, stackSize, GetRolledAffixId())
call SaveStr(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER, stackSize, GetRolledAffixType())
call SaveReal(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER, stackSize, GetRolledAffixAmount())
//
// Detect the best rolled affix so far.
if (quality > bestQuality) or (bestQuality == 0) or ((quality == bestQuality) and (GetRandomInt(0, 1) == 0)) then
call SaveReal(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER, 0x186A0, quality)
call SaveStr(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER, 0x186A0, GetRolledAffixType())
call SaveInteger(Inventory_GetTable(), INVENTORY_KEY_ITEM_PARSER, 0x186A0, GetRolledAffixId())
endif
//
call SetStackSize(stackSize + 1)
endfunction
// Randomizes affixes for item handles.
private function RollAffixes takes nothing returns nothing
local item temp = GetItem()
local integer itemId = GetItemTypeId(temp)
local integer depth
local integer count
local integer max
//
// Roll all constant affixes.
if ItemHasConstantAffixes(itemId) then
call PrepareConstantAffixRoll(itemId)
loop
exitwhen not RollConstantAffix()
call SaveData()
endloop
endif
//
// Roll random affixes of all loop instances.
if ItemHasRandomAffixes(itemId) then
set depth = 0
loop
exitwhen not AffixTableHasLoopDepth(itemId, depth)
set count = RollAffixCount(itemId, depth)
// Quality measuring. Rolled vs. maximum possible affixes.
set max = GetAffixMaxCount(itemId, depth)
if (max > 0) then
call SetQuality(GetQuality() + count/max)
call SetQualityDivisor(GetQualityDivisor() + 1)
endif
//
call PrepareRandomAffixRoll(itemId, depth)
loop
exitwhen (count <= 0)
exitwhen not RollRandomAffix()
call SaveData()
set count = count - 1
endloop
set depth = depth + 1
endloop
endif
set temp = null
endfunction
private function ParseItem takes item temp returns nothing
call PrepareParser(temp)
//
call ForForce(bj_FORCE_PLAYER[0], function RollAffixes)
call ForForce(bj_FORCE_PLAYER[0], function RegisterAffixes)
call ForForce(bj_FORCE_PLAYER[0], function MeasureQuality)
//
// Flushes literally everything the parser saved
// during the three function calls above!
call Inventory_FlushChild(INVENTORY_KEY_ITEM_PARSER)
endfunction
//===================================================================================
// Event response for custom items.
//===================================================================================
// EVENT_CUSTOM_ITEM_CREATE.
private function OnCreate takes nothing returns nothing
local item temp = GetTriggerCustomItem().getItem()
local integer itemId = GetItemTypeId(temp)
//
// Transfers all static affixes registered on the item id
// to the handle id of temp.
call ImportCustomItemAffixes(temp)
//
// Global condition for dynamic items.
if ItemRollsAffixes(itemId) then
call ParseItem(temp)
endif
call UpdateCustomItemTooltip(temp)
set temp = null
endfunction
// EVENT_CUSTOM_ITEM_DESTROY
private function OnDestroy takes nothing returns nothing
local CustomItem id = GetTriggerCustomItem()
call FlushItemTooltip(id)
call FlushCustomItemAffixes(id)
endfunction
private struct I extends array
private static method init takes nothing returns nothing
call RegisterCustomItemEvent(EVENT_CUSTOM_ITEM_CREATE, function OnCreate)
call RegisterCustomItemEvent(EVENT_CUSTOM_ITEM_DESTROY, function OnDestroy)
endmethod
implement UIInit
endstruct
endlibrary
//TESH.scrollpos=156
//TESH.alwaysfold=0
library CustomItemTooltip/* v1.0
*************************************************************************************
*
* CustomItemTooltip handles tooltips for the "CustomItem" type.
* Each tooltip is stored in lines and can be loaded row by row.
*
* Exampe:
* row 1 - "Short Axe"
* row 2 - " "
* row 3 - "+5 Damage"
* row 4 - " "
* row 5 - "Sell Value 75 Gold"
*
*************************************************************************************
*
* */ uses /*
*
* */ optional Ascii /*
* */ optional WordWrap /*
* */ optional StringSize /*
* */ optional ErrorMessage /*
*
************************************************************************************
*/
//* Pointers:
//* ==============
//* Pointers point to a segment of the tooltip.
//* You can use them to change, add, remove rows within the tooltip,
//* while obtaining the structure of the whole tooltip.
//* You can also flush a whole pointer, if it shouldn't be in the tooltip anymore.
//*
//* Pointers start with the index 1 and end with x.
//* This list from 1 to 9 is just an example. Use as many as you need.
//* Give them any name, which fits to your logic.
globals
//* Always start with 1! 0 will not work.
constant integer TOOLTIP_POINTER_HEADLINE = 1
constant integer TOOLTIP_POINTER_RUNE = 2
constant integer TOOLTIP_POINTER_BONUS_LIST = 3// ( Hardcoded inside the item parser )
constant integer TOOLTIP_POINTER_ABILITY_LIST = 4// ( Hardcoded inside the item parser )
constant integer TOOLTIP_POINTER_SOCKETS = 5
constant integer TOOLTIP_POINTER_ITEM_SET = 6
constant integer TOOLTIP_POINTER_STORY = 7
constant integer TOOLTIP_POINTER_DURABILITY = 8
constant integer TOOLTIP_POINTER_REQUIREMENTS = 9
endglobals
//* Structure:
//* ==========
//* The system loops over all pointers, so please define,
//* which pointer is the first and which is the last.
globals
constant integer ITEM_TOOLTIP_FIRST_POINTER = 1
constant integer ITEM_TOOLTIP_LAST_POINTER = 9
endglobals
//* CustomizePointers:
//* ==================
//* Here you can define, if a healine should be added between to pointers.
//* Furthermore you can set if no space should be added between to pointers.
//* Only applies if the tooltip has a value for the specified pointer.
globals
private string array headline //* By default no headline text is added between to pointers.
private boolean array nospace //* By default the system adds a space between to pointers.
endglobals
private function CustomizePointers takes nothing returns nothing
set nospace[TOOLTIP_POINTER_ABILITY_LIST] = true//* In the example no space between primary affix and affixes is added.
set headline[TOOLTIP_POINTER_BONUS_LIST] = "Primary:"
set headline[TOOLTIP_POINTER_ABILITY_LIST] = "Secondary:"
// ....
endfunction
//* Here starts the CustomItemTooltip code.
//* =======================================
globals
private constant hashtable TABLE = InitHashtable()
private constant integer OFFSET = 0x64 //* Number of high value. Available rows per pointer.
private constant integer ICONS = 0x186A0//* Number of higher value. Plus one dimension for icons.
endglobals
//* API:
//* ====
//*
//* Get:
//* The total amount of lines.
function GetItemTooltipSize takes integer itemIndex returns integer
return LoadInteger(TABLE, 0, itemIndex)
endfunction
//* A single line.
function GetItemTooltipFragment takes integer itemIndex, integer pos returns string
return LoadStr(TABLE, itemIndex, pos)
endfunction
//* A Icon for the specified line.
function GetItemTooltipFragmentIcon takes integer itemIndex, integer pos returns string
return LoadStr(TABLE, itemIndex, pos + ICONS)
endfunction
//* Pointer used in the line.
function GetItemTooltipFragmentPointer takes integer itemIndex, integer pos returns integer
return LoadInteger(TABLE, itemIndex, pos + ICONS)
endfunction
function GetItemTooltipPointerSize takes integer itemIndex, integer pointer returns integer
return LoadInteger(TABLE, itemIndex, pointer)
endfunction
function GetItemTooltipMaxWidth takes integer itemIndex returns real
return LoadReal(TABLE, 0, itemIndex)
endfunction
//*
//* Set:
//* A text in a line. Uses pointers.
function SetItemTooltipFragment takes integer itemIndex, integer pos, string text, string icon, integer pointer returns nothing
local integer size = IMaxBJ(LoadInteger(TABLE, itemIndex, pointer), pos + 1)
call SaveStr(TABLE, itemIndex, pos + pointer*OFFSET, text)
call SaveStr(TABLE, itemIndex, pos + pointer*OFFSET + ICONS, icon)
call SaveInteger(TABLE, itemIndex, pointer, size)
endfunction
//* Word wrap a whole text block to a tooltip.
function WordWrapStringToItemTooltip takes integer itemIndex, string text, real stringWidth, integer pointer returns nothing
static if LIBRARY_WordWrap then
local integer index = 0
call WordWrapString(text, stringWidth, true)
loop
exitwhen index == GetWrappedStringCount()
call SetItemTooltipFragment(itemIndex, index, GetWrappedStringFragment(index), null, pointer)
set index = index + 1
endloop
endif
endfunction
//*
//* Remove:
//* A single line of a specific pointer
function RemoveItemTooltipFragment takes integer itemIndex, integer pos, integer pointer returns nothing
call RemoveSavedString(TABLE, itemIndex, pos + pointer*OFFSET)
call RemoveSavedString(TABLE, itemIndex, pos + pointer*OFFSET + ICONS)
endfunction
//* All lines of a specific pointer.
function FlushItemTooltipPointer takes integer itemIndex, integer pointer returns nothing
local integer index = 0
local integer size = LoadInteger(TABLE, itemIndex, pointer)
loop
exitwhen (index == size)
call RemoveSavedString(TABLE, itemIndex, index + pointer*OFFSET)
call RemoveSavedString(TABLE, itemIndex, index + pointer*OFFSET + ICONS)
set index = index + 1
endloop
call RemoveSavedInteger(TABLE, itemIndex, pointer)
endfunction
//*
//* Flush:
//* A complete tooltip
function FlushItemTooltip takes integer itemIndex returns nothing
call RemoveSavedInteger(TABLE, 0, itemIndex)
call FlushChildHashtable(TABLE, itemIndex)
endfunction
//*
//* Clear: Not as radical as flush.
private function ClearTooltip takes integer itemIndex returns nothing
local integer size = LoadInteger(TABLE, 0, itemIndex)
local integer index = 0
loop
exitwhen index == size
call RemoveSavedString(TABLE, itemIndex, index)
call RemoveSavedString(TABLE, itemIndex, index + ICONS)
call RemoveSavedInteger(TABLE, itemIndex, index + ICONS)
set index = index + 1
endloop
endfunction
//* Average char length without library StringSize is set to 13.
private function Concatenate takes integer itemIndex returns nothing
local integer pointer = ITEM_TOOLTIP_FIRST_POINTER
local integer size = 0
local integer line
local integer lines
local boolean entry
local string text
local real length = 0.
//* Let's start!
loop
exitwhen (pointer > ITEM_TOOLTIP_LAST_POINTER)
//* Strings per pointer.
set line = 0
set lines = LoadInteger(TABLE, itemIndex, pointer)
set entry = (lines > 0)
if (entry) and not (pointer == ITEM_TOOLTIP_LAST_POINTER) and (size != 0) then
//* Space?
if not (nospace[pointer]) or (LoadInteger(TABLE, itemIndex, pointer - 1) == 0) then
call SaveStr(TABLE, itemIndex, size, " ")
call SaveInteger(TABLE, itemIndex, size + ICONS, 0)
set size = size + 1
endif
endif
//* Headline.
if (entry) and (headline[pointer] != null) then
set text = headline[pointer]
static if LIBRARY_StringSize then
set length = RMaxBJ(MeasureString(text), length)
else
set length = RMaxBJ(StringLength(text)*13, length)
endif
call SaveStr(TABLE, itemIndex, size, text)
call SaveInteger(TABLE, itemIndex, size + ICONS, 0)
set size = size + 1
endif
//* Tooltip block of "pointer".
loop
exitwhen (line == lines)
if HaveSavedString(TABLE, itemIndex, line + pointer*OFFSET) then
//* Text and icon.
set text = LoadStr(TABLE, itemIndex, line + pointer*OFFSET)
static if LIBRARY_StringSize then
set length = RMaxBJ(MeasureString(text), length)
else
set length = RMaxBJ(StringLength(text)*13, length)
endif
call SaveStr(TABLE, itemIndex, size, text)
call SaveStr(TABLE, itemIndex, size + ICONS, LoadStr(TABLE, itemIndex, line + pointer*OFFSET + ICONS))
//* Which type of pointer.
call SaveInteger(TABLE, itemIndex, size + ICONS, pointer)
set size = size + 1
else
//* Null string is ignored.
endif
set line = line + 1
endloop
set pointer = pointer + 1
endloop
//* Total string size:
call SaveInteger(TABLE, 0, itemIndex, size)
call SaveReal(TABLE, 0, itemIndex, length)
endfunction
function GenerateItemTooltip takes integer itemIndex returns nothing
call ClearTooltip(itemIndex)
call Concatenate(itemIndex)
endfunction
//* Seriously?
private module Inits
private static method onInit takes nothing returns nothing
static if LIBRARY_ErrorMessage then
debug call ThrowError(ITEM_TOOLTIP_FIRST_POINTER <= 0, "CustomItemTooltip", "Init", "FIRST_POINTER", ITEM_TOOLTIP_FIRST_POINTER, "ITEM_TOOLTIP_FIRST_POINTER has to be bigger than 0.")
debug call ThrowError(ITEM_TOOLTIP_LAST_POINTER < ITEM_TOOLTIP_FIRST_POINTER, "CustomItemTooltip", "Init", "LAST_POINTER", ITEM_TOOLTIP_LAST_POINTER, "ITEM_TOOLTIP_LAST_POINTER has to be bigger than ITEM_TOOLTIP_FIRST_POINTER.")
endif
call CustomizePointers()
endmethod
endmodule
private struct I extends array
implement Inits
endstruct
endlibrary
//TESH.scrollpos=128
//TESH.alwaysfold=0
library CustomItemAffixList uses InventoryCore
//=========================================================================
// Item affixes are effects an item grants to its carrier unit.
// These affixes can be of two types:
// 1.) ability id ( inlcuding ability levels )
// 2.) BonusMod Bonus ( refer to: wc3c.net/showthread.php?t=107940 )
//=========================================================================
// CustomItemAffixList saves all affixes for item ids
// and item type ids in seperate lists.
// Only affixes of item id lists are added to units,
// while the item type id lists transfer their nodes
// to item ids lists when a new CustomItem is created.
//=========================================================================
// In order to avoid code collision inside the hashtable,
// there are all together four parent keys,
// which determine the type of affix list.
// 1.) INVENTORY_ITEM_TYPE_ID_ABILITY_LIST
// 2.) INVENTORY_ITEM_TYPE_ID_BONUS_LIST
// 3.) INVENTORY_ITEM_ID_ABILITY_LIST
// 4.) INVENTORY_ITEM_ID_BONUS_LIST
//
// Also two parent keys to distinguish affix id and
// affix quantity between item type ids and item ids.
// A.) INVENTORY_ITEM_TYPE_ID_AFFIXES
// B.) INVENTORY_ITEM_ID_AFFIXES
//
// Affix ids are saved on the + value of a node.
// Affix quantities are saved on the - value of a node.
//========================================================================
//========================================================================
// Saving affixes to lists.
//========================================================================
// Bonus or ability.
private function SearchNodeMatch takes integer affixType, integer list, integer affix returns integer
local integer node = Inventory_GetListFirstNode(list)
loop
exitwhen 0 == node
if LoadInteger(Inventory_GetTable(), affixType, node) == affix then
return node
endif
set node = Inventory_GetListNextNode(node)
endloop
return 0
endfunction
// Bonus or ability.
private function SaveAffix takes integer affixType, integer parent, integer itemKey, integer affix, integer amount returns nothing
local integer list = Inventory_GetListByKey(parent, itemKey)
local integer node = SearchNodeMatch(affixType, list, affix)
local integer sum = LoadInteger(Inventory_GetTable(), affixType, -node) + amount
if node == 0 then
if amount != 0 then
set node = Inventory_EnqueueToList(list)
call SaveInteger(Inventory_GetTable(), affixType, node, affix)
call SaveInteger(Inventory_GetTable(), affixType, -node, amount)
endif
elseif sum == 0 then
call RemoveSavedInteger(Inventory_GetTable(), affixType, node)
call RemoveSavedInteger(Inventory_GetTable(), affixType, -node)
call Inventory_RemoveNodeFromList(node)
else
call SaveInteger(Inventory_GetTable(), affixType, -node, sum)
endif
endfunction
// For read-ability only.
private function SaveItemTypeIdAffix takes integer parent, integer itemKey, integer affix, integer amount returns nothing
call SaveAffix(INVENTORY_ITEM_TYPE_ID_AFFIXES, parent, itemKey, affix, amount)
endfunction
private function SaveItemIdAffix takes integer parent, integer itemKey, integer affix, integer amount returns nothing
call SaveAffix(INVENTORY_ITEM_ID_AFFIXES, parent, itemKey, affix, amount)
endfunction
//========================================================================
// Adding & removing affixes to units.
//========================================================================
static if not LIBRARY_BonusMod and not LIBRARY_Bonus then
// Required system is not present.
else // + or -
private function AddBonuses takes unit source, integer affixType, integer id, integer signum returns nothing
local integer list = Inventory_GetListByKey(INVENTORY_ITEM_ID_BONUS_LIST, id)
local integer node = Inventory_GetListFirstNode(list)// List of an item handle id.
local integer bonus
local integer amount
loop
exitwhen 0 == node
set bonus = LoadInteger(Inventory_GetTable(), affixType, node)
set amount = LoadInteger(Inventory_GetTable(), affixType, -node)*signum
call AddUnitBonus(source, bonus, amount)
set node = Inventory_GetListNextNode(node)
endloop
endfunction
endif
// GetHandleId(item)
private function AddAbilities takes unit source, integer affixType, integer id returns nothing
local integer list = Inventory_GetListByKey(INVENTORY_ITEM_ID_ABILITY_LIST, id)
local integer node = Inventory_GetListFirstNode(list)
local integer abilityId
local integer level
loop
exitwhen 0 == node
set abilityId = LoadInteger(Inventory_GetTable(), affixType, node)
set level = LoadInteger(Inventory_GetTable(), affixType, -node)
if UnitAddAbility(source, abilityId) then
call SetUnitAbilityLevel(source, abilityId, level)
call UnitMakeAbilityPermanent(source, true, abilityId)
// The ability was already on the unit.
else
set level = level + GetUnitAbilityLevel(source, abilityId)
if level != SetUnitAbilityLevel(source, abilityId, level) then
debug call ThrowWarning(true, "CustomItemAffixList", "AddAbilities", "level", 0, "The map doesn't support for " + GetObjectName(abilityId) + " a level of " + I2S(level))
endif
endif
set node = Inventory_GetListNextNode(node)
endloop
endfunction
// GetHandleId(item)
private function RemoveAbilities takes unit source, integer affixType, integer id returns nothing
local integer list = Inventory_GetListByKey(INVENTORY_ITEM_ID_ABILITY_LIST, id)
local integer node = Inventory_GetListFirstNode(list)
local integer abilityId
local integer level
loop
exitwhen 0 == node
set abilityId = LoadInteger(Inventory_GetTable(), affixType, node)
set level = LoadInteger(Inventory_GetTable(), affixType, -node)
if GetUnitAbilityLevel(source, abilityId) <= level then
call UnitRemoveAbility(source, abilityId)
else
call SetUnitAbilityLevel(source, abilityId, GetUnitAbilityLevel(source, abilityId) - level)
endif
set node = Inventory_GetListNextNode(node)
endloop
endfunction
//========================================================================
// Public API called from Inventory on equip and unequip event.
//========================================================================
function RemoveUnitItemAffixes takes unit source, item whichItem returns nothing
local integer id = GetHandleId(whichItem)
if (id != 0) and (GetUnitTypeId(source) != 0) then
call RemoveAbilities(source, INVENTORY_ITEM_ID_AFFIXES, id)
static if not LIBRARY_BonusMod and not LIBRARY_Bonus then
// Required system is not present.
else
call AddBonuses(source, INVENTORY_ITEM_ID_AFFIXES, id, -1)
endif
endif
endfunction
function AddUnitItemAffixes takes unit source, item whichItem returns nothing
local integer id = GetHandleId(whichItem)
if (id != 0) and (GetUnitTypeId(source) != 0) then
call AddAbilities(source, INVENTORY_ITEM_ID_AFFIXES, id)
static if not LIBRARY_BonusMod and not LIBRARY_Bonus then
// Required system is not present.
else
call AddBonuses(source, INVENTORY_ITEM_ID_AFFIXES, id, 1)
endif
endif
endfunction
//========================================================================
// User API; Documentated in Inventory API.
//========================================================================
// These abilities are always present on the ability list for items of type "itemId".
function AddCustomItemIdAbility takes integer itemId, integer abilityId, integer level returns nothing
debug call ThrowError(itemId == 0, "CustomItemAffixList", "AddCustomItemIdAbility", "itemId", 0, "Invalid item id ( 0 )!")
call SaveItemTypeIdAffix(INVENTORY_ITEM_TYPE_ID_ABILITY_LIST, itemId, abilityId, level)
endfunction
// These bonuses are always present on the bonus list for items of type "itemId".
function AddCustomItemIdBonus takes integer itemId, integer bonus, integer amount returns nothing
debug call ThrowError(itemId == 0, "CustomItemAffixList", "AddCustomItemIdAbility", "itemId", 0, "Invalid item id ( 0 )!")
call SaveItemTypeIdAffix(INVENTORY_ITEM_TYPE_ID_BONUS_LIST, itemId, bonus, amount)
endfunction
// These abilities are only present on the ability list of a specific item.
function AddCustomItemAbility takes item i, integer abilityId, integer level returns nothing
if (GetItemTypeId(i) != 0) then
call SaveItemIdAffix(INVENTORY_ITEM_ID_ABILITY_LIST, GetHandleId(i), abilityId, level)
endif
endfunction
// These bonuses are only present on the bonus list of a specific item.
function AddCustomItemBonus takes item i, integer bonus, integer amount returns nothing
if (GetItemTypeId(i) != 0) then
call SaveItemIdAffix(INVENTORY_ITEM_ID_BONUS_LIST, GetHandleId(i), bonus, amount)
endif
endfunction
// Completely empties all lists for an item handle id.
function FlushCustomItemAffixes takes integer id returns nothing
debug call ThrowWarning(id == 0, "CustomItemAffixList", "FlushCustomItemAffixes", "id", 0, "Invalid handle id ( 0 )!")
if (id != 0) then
call Inventory_DestroyListByKey(INVENTORY_ITEM_ID_BONUS_LIST, id)
call Inventory_DestroyListByKey(INVENTORY_ITEM_ID_ABILITY_LIST, id)
endif
endfunction
//==========================================================================
// Import from item id list to handle id list. Called from the item parser.
//==========================================================================
private function ImportBonuses takes item i returns nothing
local integer id = GetHandleId(i)
local integer list = Inventory_GetListByKey(INVENTORY_ITEM_TYPE_ID_BONUS_LIST, GetItemTypeId(i))
local integer node = Inventory_GetListFirstNode(list)
local integer bonus
local integer amount
loop
exitwhen 0 == node
set bonus = LoadInteger(Inventory_GetTable(), INVENTORY_ITEM_TYPE_ID_AFFIXES, node)
set amount = LoadInteger(Inventory_GetTable(), INVENTORY_ITEM_TYPE_ID_AFFIXES, -node)
call SaveItemIdAffix(INVENTORY_ITEM_ID_BONUS_LIST, id, bonus, amount)
set node = Inventory_GetListNextNode(node)
endloop
endfunction
private function ImportAbilities takes item i returns nothing
local integer id = GetHandleId(i)
local integer list = Inventory_GetListByKey(INVENTORY_ITEM_TYPE_ID_ABILITY_LIST, GetItemTypeId(i))
local integer node = Inventory_GetListFirstNode(list)
local integer abilityId
local integer level
loop
exitwhen 0 == node
set abilityId = LoadInteger(Inventory_GetTable(), INVENTORY_ITEM_TYPE_ID_AFFIXES, node)
set level = LoadInteger(Inventory_GetTable(), INVENTORY_ITEM_TYPE_ID_AFFIXES, -node)
call SaveItemIdAffix(INVENTORY_ITEM_ID_ABILITY_LIST, id, abilityId, level)
set node = Inventory_GetListNextNode(node)
endloop
endfunction
function ImportCustomItemAffixes takes item i returns nothing
local integer itemId = GetItemTypeId(i)
if (itemId != 0) then
if Inventory_KeyForListExists(INVENTORY_ITEM_TYPE_ID_BONUS_LIST, itemId) then
call ImportBonuses(i)
endif
if Inventory_KeyForListExists(INVENTORY_ITEM_TYPE_ID_ABILITY_LIST, itemId) then
call ImportAbilities(i)
endif
endif
endfunction
endlibrary
//TESH.scrollpos=29
//TESH.alwaysfold=0
library InventoryNativeInventory uses Inventory
//===================================================================================
// Native inventory sync.
// This library enables the
// custom inventory - native inventory interaction.
// This interface is freakin' perfect.
//===================================================================================
// Only items of type:
// 1.) ITEM_TYPE_CHARGED
// 2.) ITEM_TYPE_PURCHASABLE
// can be moved to cells of this struct.
//===================================================================================
struct InventoryNativeInventory extends UIWindow
// Returns the slot index of a button.
private method getSlotIndex takes UIButton temp returns integer
local integer dex = getButtonCount()
loop
exitwhen 0 == dex
set dex = dex - 1
exitwhen getButton(dex) == temp
endloop
return dex
endmethod
// Hovering a cell allocated from this struct.
private method onHover takes Inventory inventory, UIButton hovered returns nothing
call inventory.releaseTextBox()
call inventory.showItemTooltip(InventoryCell(hovered.data).getItem())
endmethod
// Clicking a cell allocated from this struct.
private method onClick takes Inventory inventory, UIButton clicked returns nothing
local InventoryCell sourceCell = inventory.selected
local InventoryCell targetCell = clicked.data
local integer index = getSlotIndex(clicked)
local item targetItem = targetCell.getItem()
local item sourceItem = sourceCell.getItem()
local unit sourceUnit = inventory.source
if sourceItem != null then
//==============================================
// The previous clicked cell
// was allocated from this struct.
//==============================================
if thistype.typeid == sourceCell.getTypeId() then
if StackChargedItems(targetItem, sourceItem) then
call audio.search(GetItemTypeId(sourceItem))
call inventory.showItemTooltip(targetItem)
elseif sourceItem != UnitItemInSlot(sourceUnit, index) then
if UnitDropItemSlot(sourceUnit, sourceItem, index) then
call audio.search(GetItemTypeId(sourceItem))
call inventory.showItemTooltip(sourceItem)
endif
endif
//====================================================
// Previous click was in the backpack interface.
//====================================================
elseif sourceCell.getTypeId() == GetBackpackTypeId() then
if inventory.swapCustomNative(sourceCell, targetCell, index) then
call audio.search(GetItemTypeId(sourceItem))
call inventory.showItemTooltip(sourceItem)
endif
endif
call inventory.deselect()
elseif targetItem != null then
call inventory.select(targetCell)
endif
call audio.mouseClick()
set targetItem = null
set sourceItem = null
set sourceUnit = null
endmethod
private static method onCreate takes Inventory inventory returns thistype
local unit source = inventory.source
local real sizeX = RMaxBJ(Inventory_GetBackpackColumnCount(source)*32, 64*3.5)
local thistype this = thistype.create(inventory, inventory.width*.5 + sizeX, 64*1.5, 128., 192.)
local string img = Inventory_GetBackpackSlotFilePath(source)
local integer size = UnitInventorySize(source)
local integer row = 0
local integer column
//======================
// Inventory grid:
// ===============
// 0 1
// 2 3
// 4 5
//=======================
loop
exitwhen row == 3
set column = 0
loop
exitwhen column == 2
if (size > row*2 + column) then
set addImageButton(TRACK_64x64, 32 + column*64, 64*2.5 - 64*row, 0., 270., img, 1.).data = ItemClass.ANY
else
call addBorderImage(Inventory_GetSlotFiller(source), column*64, 64*2 - 64*row, 64, 64, IMAGE_TYPE_INDICATOR)
endif
set column = column + 1
endloop
set row = row + 1
endloop
call Inventory_SetBackpackBorderFields(source)
call frame(1.)
// Assign InventoryCells to allocated buttons.
call inventory.initInventoryCells(this)
set source = null
return this
endmethod
private method onDestroy takes nothing returns nothing
local integer dex = getButtonCount()
local InventoryCell cell
loop
exitwhen 0 == dex
set dex = dex - 1
set cell = getButton(dex).data
if cell.exists then
call cell.destroy()
endif
endloop
endmethod
// Enables the Inventory interface for this struct.
implement InventoryStruct
endstruct
endlibrary
//TESH.scrollpos=120
//TESH.alwaysfold=0
library InventoryDummy initializer Init uses Inventory
//===================================================================================
// The dummy interface is a window for the dummy unit.
// Adding a dummy unit to an Inventory instance is optional.
// Clicking the window will reveal or hide character stats.
//===================================================================================
struct InventoryDummy extends UIWindow
private UIBorder box
private textsplat affixes
private textsplat headline
readonly boolean visible
// Updates the textbox.
private method updateBoxContent takes nothing returns nothing
local Inventory inv = screen
local unit source = inv.source
local real posX = inv.originX + inv.width*.5 + 360.
local integer dex = 1
local integer end = Inventory_GetAffixBoxRowCount(source)
local string str = Inventory_GetAffixBoxRowString(source, 0)
// Preparing affixes string.
loop
exitwhen dex >= end
set str = str + "\n" + Inventory_GetAffixBoxRowString(source, dex)
set dex = dex + 1
endloop
// Displaying headline textsplat.
call headline.setText(Inventory_GetAffixBoxHeadline(source), INVENTORY_FONT_SIZE, TEXTSPLAT_TEXT_ALIGN_CENTER)
call headline.setPosition(posX - headline.width*.5, originY + height + 32., 0.)
call headline.setVisible(visible and GetLocalClient() == user)
// Displaying affixes textsplat.
call affixes.setText(str, INVENTORY_FONT_SIZE*.75, TEXTSPLAT_TEXT_ALIGN_LEFT)
call affixes.setPosition(posX - RMaxBJ(affixes.width*.5, 113.889), originY + height - affixes.height, 0)
call affixes.setVisible(visible and GetLocalClient() == user)
set source = null
endmethod
method updateBox takes boolean doShow returns nothing
local Inventory inventory
if box == 0 then
return
endif
// Clear previous splats.
if affixes != 0 then
call DestroyTextSplat(affixes)
set affixes = 0
endif
if headline != 0 then
call DestroyTextSplat(headline)
set affixes = 0
endif
call box.show(doShow and GetLocalClient() == user)
if doShow then
set inventory = screen
set affixes = CreateTextSplat(inventory.getFont())
set headline = CreateTextSplat(inventory.getFont())
call SetTextSplatSuspended(affixes, true)
call SetTextSplatSuspended(headline, true)
call updateBoxContent()
endif
endmethod
// Runs when a cell of this struct is hovered.
private method onHover takes Inventory inventory, UIButton clicked returns nothing
call inventory.releaseTextBox()
call board.release()// Releases the current item tooltip.
if visible and box != 0 then
call inventory.timedMsg("Click to hide details", 2.)
elseif box != 0 then
call inventory.timedMsg("Click to show details", 2.)
endif
endmethod
// Runs when a cell of this struct is clicked.
private method onClick takes Inventory inventory, UIButton clicked returns nothing
if 0 != box then
set visible = not visible
call updateBox(visible)
if visible then
call inventory.timedMsg("Click to hide details", 2.)
else
call inventory.timedMsg("Click to show details", 2.)
endif
//
call audio.searchSoundFile("Sound\\Interface\\QuestActivateWhat1.wav", 10, .54)
endif
endmethod
// Runs when an Inventory interface is opened or closed.
private method onShow takes Inventory inventory, boolean flag returns nothing
local unit dummy = inventory.dummy
if dummy != null then
if flag then
call SetUnitX(dummy, originX + width*.5)
call SetUnitY(dummy, originY + inventory.dummyY)// Individual adjustment.
else
call SetUnitPosition(dummy, WorldBounds.maxX, WorldBounds.maxY)
endif
// Restore locust.
call ShowUnit(dummy, flag)
call UnitRemoveAbility(dummy, 'Aloc')
call UnitAddAbility(dummy, 'Aloc' )
set dummy = null
endif
call updateBox(flag and visible)
endmethod
// Creates the window. The position is realtive to the backpack height.
private static method onCreate takes Inventory inventory returns thistype
local unit source = inventory.source
local real tile = 64.
local real posY = tile*2.5 + Inventory_GetBackpackRowCount(source)*tile + tile*1.375
local thistype this = thistype.create(inventory, inventory.width*.5 - tile*1.125, posY, tile*2.25, tile*2.25)
if (Inventory_AllocateAffixBoxForUnit(source)) then
//===================================================================================
// Rotating a dummy on an UI camera angle is very difficult.
// I came up with an alternative idea of displaying stats, when clicking the dummy.
// For track detection, I created an image button with a null model file path.
//===================================================================================
call addImageButton(TRACK_128x128, width*.5, height*.5, 0., 270., null, 0.)
call Inventory_SetAffixBoxBorderFields(source)
set box = UIBorder.create().construct(inventory.originX + inventory.width*.5 + tile*5.625, inventory.originY + posY + tile*1.125, tile*4, tile*5 + 4. + 4, 1.)
endif
call Inventory_SetDummyBorderFields(source)
call frame(.75)
set visible = false
set source = null
return this
endmethod
// As clean as it gets.
private method onDestroy takes nothing returns nothing
if box != 0 then
call box.destroy()
set box = 0
endif
endmethod
implement InventoryStruct
endstruct
//===================================================================================
// OnEquip and OnUnequip Event Response.
//===================================================================================
// Inventory doesn't use priority event based trigger evaluation.
// Therefore the affix box update is done via a 0 seconds timer callback.
// As the affix details are pure visual ( no getters ), a delay is totally acceptable.
globals
private boolean array run
private integer array next
endglobals
// API
function Inventory_UpdateAffixBox takes unit source returns nothing
local Inventory data = GetUnitInventory(source)
local InventoryDummy box = data.getWindowOfType(InventoryDummy.typeid)
if box.visible and box.enabled then
call box.updateBox(true)
endif
endfunction
private function Callback takes nothing returns nothing
local Inventory data = next[0]
call DestroyTimer(GetExpiredTimer())
loop
exitwhen 0 == data
if data.enabled then
call Inventory_UpdateAffixBox(data.source)
endif
set run[data] = false
set data = next[data]
endloop
set next[0] = 0
endfunction
private function OnEvent takes nothing returns nothing
local Inventory data = GetTriggerInventory()
if 0 == next[0] then
call TimerStart(CreateTimer(), 0, false, function Callback)
endif
if not run[data] then
set run[data] = true
set next[data] = next[0]
set next[0] = data
endif
endfunction
private function Init takes nothing returns nothing
call RegisterInventoryEvent(EVENT_INVENTORY_UNIT_EQUIP_ITEM, function OnEvent)
call RegisterInventoryEvent(EVENT_INVENTORY_UNIT_UNEQUIP_ITEM, function OnEvent)
endfunction
endlibrary
//TESH.scrollpos=162
//TESH.alwaysfold=0
// Written by BPower.
library InventoryBackpack uses Inventory
//===================================================================================
// Backpack interface.
// This library creates a backpack surface.
// In order to configurate the button position
// go to static method onCreate at the bottom.
//===================================================================================
struct InventoryBackpack extends UIWindow
private integer selectedPage
// Runs when hovering a button of this struct.
private method onHover takes Inventory inventory, UIButton hovered returns nothing
call inventory.releaseTextBox()
call inventory.showItemTooltip(InventoryCell(hovered.data).getItem())
endmethod
// Runs when clicking a button of this struct.
private method onClick takes Inventory inventory, UIButton clicked returns nothing
local InventoryCell targetCell = clicked.data
local InventoryCell sourceCell = inventory.selected
local item sourceItem = sourceCell.getItem()
local item targetItem = targetCell.getItem()
//====================================
// Same backpack cell clicked twice.
//====================================
if (sourceCell == targetCell) then
if (sourceItem != null) then
// Detect a double click.
if isDoubleClick() then
// Try to equip the clicked item.
if inventory.unitEquipItem(targetItem) then
call board.release()// Releases the previous tooltip.
call audio.search(GetItemTypeId(targetItem))
else
// Equip process failed. ( No cell available or all cells taken )
call inventory.errorMsg(GetUnitName(inventory.source) + ": I cannot do this!")
endif
endif
call inventory.deselect()
endif
set sourceItem = null
set targetItem = null
return// No need to go further.
endif
//====================================
// Previous selected cell has an item.
//====================================
if (sourceItem != null) then
//========================================================
// Previous click was also within the backpack interface.
//========================================================
if (thistype.typeid == sourceCell.getTypeId()) then
// Check for item sockets. ( Not implemented yet )
if inventory.socketItem(targetItem, sourceItem) then
//
// Check for item charges. ( target remains in case of a complete merging )
elseif inventory.mergeItems(targetItem, sourceItem) then
call audio.search(GetItemTypeId(targetItem))
call inventory.showItemTooltip(targetItem)// Update the tooltip.
//
// Swap cell contents.
elseif (inventory.swapCellContents(sourceCell, targetCell)) then
call inventory.showItemTooltip(sourceItem)
call audio.search(GetItemTypeId(sourceItem))
//
// Undefined backpack action.
else
// debug call ThrowWarning(true, "Backpack", "?", "?", this, "Undefined backpack action for items. Yet unsolved!")
call audio.error()
endif
//========================================================
// Previous click was in the equipment interface.
//========================================================
elseif (sourceCell.getTypeId() == GetEquipmentTypeId()) then
// Clicked cell has an item.
if (targetItem != null) then
//
// Try to equip the item into the cell the previous item ( source ) was.
if (inventory.unitEquipItemToSlot(targetItem, sourceCell, true)) then
call audio.search(GetItemTypeId(sourceItem))
call inventory.showItemTooltip(sourceItem)
else
set targetItem = null
set sourceItem = null
return// No need to go further.
endif
//
// Clicked cell is empty. Unequip from equipment to the target cell.
elseif inventory.unitUnequipItemToSlot(sourceItem, targetCell) then
call audio.search(GetItemTypeId(sourceItem))
call inventory.showItemTooltip(sourceItem)
endif
//========================================================
// Previous click was in the native UI sync interface.
//========================================================
elseif sourceCell.getTypeId() == GetNativeInventoryTypeId() then
if inventory.swapCustomNative(targetCell, sourceCell, inventory.getItemSlotIndex(sourceItem)) then
call audio.search(GetItemTypeId(sourceItem))
call inventory.showItemTooltip(sourceItem)
endif
endif
call inventory.deselect()
//============================
// Clicked cell has an item.
//============================
elseif (targetItem != null) then
set selectedPage = page
call inventory.select(targetCell)
endif
//
// Mouse click!
call audio.mouseClick()
set sourceItem = null
set targetItem = null
endmethod
// Runs when a page from the backpack interface is shown.
private method onShowPage takes Inventory inventory, boolean flag returns nothing
if (inventory.selected.getTypeId() == thistype.typeid) then
call ShowDestructable(inventory.selector, ((flag) and (selectedPage == page) and (GetLocalClient() == user)))
endif
if (flag) and (pages > 1) then
call inventory.timedMsg("Backpack pocket " + I2S(page), 2.)
endif
endmethod
// Runs on Inventory creation.
private static method onCreate takes Inventory inventory returns thistype
local real tile = inventory.tileSize
local unit source = inventory.source
local integer rows = Inventory_GetBackpackRowCount(source)
local integer columns = Inventory_GetBackpackColumnCount(source)
local string file = Inventory_GetBackpackSlotFilePath(source)
local integer more = Inventory_GetBackpackPageCount(source) - 1
local integer column = 0
local thistype this = 0
if (columns*rows != 0) then
set this = thistype.create(inventory, inventory.width*.5 - columns*tile*.5, tile*1.5, columns*tile, rows*tile)
// Create a 2D grid, while the first cell is located left top.
loop
exitwhen (rows == 0)
set column = 0
loop
exitwhen (column == columns)
set addImageButton(TRACK_64x64, tile*.5 + column*tile, rows*tile - tile*.5, 0., 270., file, 1).data = ItemClass.ANY
set column = column + 1
endloop
set rows = rows - 1
endloop
// Frame the window.
call Inventory_SetBackpackBorderFields(source)
call frame(1.)
if (more > 0) then
call addPages(more, true, false)
endif
// Assign InventoryCells to allocated buttons.
call inventory.initInventoryCells(this)
endif
set source = null
return this
endmethod
// Runs when an Inventory instance is destroyed.
// Removes all items from the source units backpack.
private method onDestroy takes nothing returns nothing
local Inventory inv = screen
local integer dex = 0
local integer max = getButtonCount()
local InventoryCell cell
loop
exitwhen dex == max
set cell = getButton(dex).data
if (cell.exists) then
if (cell.getItem() != null) then
call inv.unitRemoveItem(cell.getItem())
endif
call cell.destroy()
endif
set dex = dex + 1
endloop
endmethod
// Enables the Inventory interface for this struct.
implement InventoryStruct
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
// Written by BPower.
library InventoryEquipment uses Inventory
//===================================================================================
// Equipment interface.
// This library creates an equipment surface
// for units using Inventory. In order to configurate the
// button position go to static method onCreate at the bottom.
//===================================================================================
struct InventoryEquipment extends UIWindow
// Runs when a button of this struct is hovered.
private method onHover takes Inventory inventory, UIButton hovered returns nothing
local InventoryCell cell = hovered.data
call inventory.releaseTextBox()
if not inventory.showItemTooltip(cell.getItem()) then
call inventory.timedMsg(cell.class.name + " slot.", 2.)
endif
endmethod
// Runs when a button of this struct is clicked.
private method onClick takes Inventory inventory, UIButton clicked returns nothing
local InventoryCell targetCell = clicked.data
local InventoryCell sourceCell = inventory.selected
local item sourceItem = sourceCell.getItem()
local item targetItem = targetCell.getItem()
//====================================
// Same equipment cell clicked twice.
//====================================
if (sourceCell == targetCell) then
if (sourceItem != null) then
if (isDoubleClick()) then
call inventory.unitUnequipItemToSlot(sourceItem, 0)
call audio.search(GetItemTypeId(sourceItem))
call board.release()
endif
call inventory.deselect()
endif
set sourceItem = null
set targetItem = null
return// No need to go further.
endif
//====================================
// Previous selected cell has an item.
//====================================
if (sourceItem != null) then
// Clicks within the Equipment UI.
if (sourceCell.getTypeId() == thistype.typeid) then
if IsCustomItemTwohand(sourceItem) or ((targetItem != null) and (IsCustomItemTwohand(targetItem))) then
call inventory.errorMsg(GetUnitName(inventory.source) + ": I can not do this!")
elseif (inventory.swapCellContents(sourceCell, targetCell)) then
call inventory.showItemTooltip(sourceItem)
call audio.search(GetItemTypeId(sourceItem))
else
call inventory.errorMsg(GetUnitName(inventory.source) + ": I can not do this!")
endif
//===========================================
// Previous selected cell is a backpack cell.
//===========================================
elseif (sourceCell.getTypeId() == GetBackpackTypeId()) then
// Check for item sockets. ( Not implemented yet )
if inventory.socketItem(targetItem, sourceItem) then
//
// Try to equip.
elseif (inventory.unitEquipItemToSlot(sourceItem, targetCell, true)) then
call inventory.showItemTooltip(sourceItem)
call audio.search(GetItemTypeId(sourceItem))
endif
//
// Undefined equipment action.
debug else
debug call ThrowWarning(true, "Equipment", "onClick", "-", this, "Undefined operation! Doing nothing!")
endif
call inventory.deselect()
//
// Target cell has an item.
elseif (targetItem != null) then
call inventory.select(targetCell)
endif
// Mouse click does not play if another sound is running.
call audio.mouseClick()
set sourceItem = null
set targetItem = null
endmethod
// Runs when an Inventory instance is created.
private static method onCreate takes Inventory inventory returns thistype
local real tile = inventory.tileSize
local real posY = tile*2.5 + Inventory_GetBackpackRowCount(inventory.source)*tile
local thistype this = thistype.create(inventory, inventory.width*.5 - tile*2.5, posY, tile*5, tile*5)
local unit source = inventory.source
//===========================================
// Equipment buttons.
//===========================================
// You can modify those to your needs. Unit source is the inventory owning unit.
//
// Button API:
// - For deststructable buttons: track, x, y, z, face, destructableId, scale
// - In case you use an image file: track, x, y, z, face, imageFilePath, scale
// Middle top.
set addDestButton(TRACK_64x64, tile*2.5, tile*4.5, 0., 270., 'Bice', 1.).data = ITEM_CLASS_HELMET
// Left.
set addDestButton(TRACK_64x64, tile*0.5, tile*0.5, 0., 270., 'Bicd', 1.).data = ITEM_CLASS_WEAPON
set addDestButton(TRACK_64x64, tile*0.5, tile*1.5, 0., 270., 'Bich', 1.).data = ITEM_CLASS_RING
set addDestButton(TRACK_64x64, tile*0.5, tile*2.5, 0., 270., 'Bicc', 1.).data = ITEM_CLASS_GLOVES
set addDestButton(TRACK_64x64, tile*0.5, tile*3.5, 0., 270., 'Bicg', 1.).data = ITEM_CLASS_WRIST
set addDestButton(TRACK_64x64, tile*0.5, tile*4.5, 0., 270., 'Bici', 1.).data = ITEM_CLASS_SHOULDER
// Right.
set addDestButton(TRACK_64x64, tile*4.5, tile*0.5, 0., 270., 'Bicd', 1.).data = ITEM_CLASS_WEAPON
set addDestButton(TRACK_64x64, tile*4.5, tile*1.5, 0., 270., 'Bich', 1.).data = ITEM_CLASS_RING
set addDestButton(TRACK_64x64, tile*4.5, tile*2.5, 0., 270., 'Bicj', 1.).data = ITEM_CLASS_PANTS
set addDestButton(TRACK_64x64, tile*4.5, tile*3.5, 0., 270., 'Bicf', 1.).data = ITEM_CLASS_CHEST
set addDestButton(TRACK_64x64, tile*4.5, tile*4.5, 0., 270., 'Bica', 1.).data = ITEM_CLASS_AMULET
// Middle bottom.
set addDestButton(TRACK_64x64, tile*3.25, tile*0.5, 0., 270., 'Bick', 1.).data = ITEM_CLASS_BELT
set addDestButton(TRACK_64x64, tile*1.75, tile*0.5, 0., 270., 'Bicb', 1.).data = ITEM_CLASS_BOOTS
// Window border settings.
call Inventory_SetEquipmentBorderFields(source)
call frameEx(tile/8, tile/8, 1.)
call inventory.initInventoryCells(this)
set source = null
return this
endmethod
// Runs when a Inventory instance is destroyed.
// Drops all items for that unit from the inventory.s
private method onDestroy takes nothing returns nothing
local Inventory inventory = screen
local integer dex = 0
local integer max = getButtonCount()
local InventoryCell cell
loop
exitwhen dex == max
set cell = getButton(dex).data
if cell.exists then
if (cell.getItem() != null) then
call inventory.unitRemoveItem(cell.getItem())
endif
call cell.destroy()
endif
set dex = dex + 1
endloop
endmethod
// Enables the Inventory interface for this struct.
implement InventoryStruct
endstruct
endlibrary
//TESH.scrollpos=42
//TESH.alwaysfold=0
// Written by BPower.
library InventoryControll uses Inventory
//================================================
// Controll panel.
//================================================
globals
//========================
// Available operations.
//========================
private key CLOSE_INVENTORY
private key DROP_SELECTION
private key SCROLL_UP_BACKPACK
private key SCROLL_DOWN_BACKPACK
endglobals
struct InventoryControll extends UIWindow
// Do not add color code to the textbox strings. WordWrap kills them anyway.
private method onHover takes Inventory inventory, UIButton hovered returns nothing
local integer action = hovered.data
local real posX = hovered.x
local real posY = hovered.y
local UIWindow window = inventory.getWindowOfType(GetBackpackTypeId())
// Clear box.
call inventory.releaseTextBox()
if action == CLOSE_INVENTORY then
call inventory.createTextBox("Click to close the inventory.", posX - 64, posY + 64, 150)
elseif action == DROP_SELECTION then
call inventory.createTextBox("Click to drop the selected item.", posX - 64, posY + 64, 150)
elseif action == SCROLL_UP_BACKPACK then
if (window.page - 1 != 0) then
call inventory.createTextBox("Next pocket.", posX - 81, posY + 64, 120)
endif
elseif action == SCROLL_DOWN_BACKPACK then
if window.page < window.pages then
call inventory.createTextBox("Previous pocket.", posX - 96, posY + 64, 150)
endif
endif
endmethod
// Colorizes the prev and next pocket arrows when opening the UI.
// onShow runs before all images are drawn, but luckily onShowPage runs after.
private method onShowPage takes Inventory inventory, boolean flag returns nothing
if flag and Inventory_GetBackpackPageCount(inventory.source) > 1 then
call SetImageColor(getButton(1).img, 50, 50, 50, 255)
call SetImageColor(getButton(2).img, 255, 255, 255, 255)
endif
endmethod
private method onClick takes Inventory inventory, UIButton clicked returns nothing
local InventoryCell sourceCell = inventory.selected
local integer action = clicked.data
local item sourceItem = sourceCell.getItem()
local UIWindow window = inventory.getWindowOfType(GetBackpackTypeId())
// A click releases the info textbox, as I assume
// the player understood the consequense of the action.
call inventory.releaseTextBox()
if action == CLOSE_INVENTORY then
call inventory.show(false)
elseif action == SCROLL_UP_BACKPACK then
if window.prevPage() then
call SetImageColor(getButton(2).img, 255, 255, 255, 255)
call audio.searchSoundFile("Sound\\Interface\\PickUpItem.wav", 10, .174)
if window.page == 1 then
call SetImageColor(clicked.img, 50, 50, 50, 255)
else
call inventory.createTextBox("Next pocket.", clicked.x - 81, clicked.y + 64, 120)
endif
else
call audio.error()
endif
elseif action == SCROLL_DOWN_BACKPACK then
if window.nextPage() then
call SetImageColor(getButton(1).img, 255, 255, 255, 255)
call audio.searchSoundFile("Sound\\Interface\\PickUpItem.wav", 10, .174)
if window.page == window.pages then
call SetImageColor(clicked.img, 50, 50, 50, 255)
else
call inventory.createTextBox("Previous pocket.", clicked.x - 96, clicked.y + 64, 150)
endif
else
call audio.error()
endif
elseif action == DROP_SELECTION then
if sourceItem != null then
if sourceCell.getTypeId() == GetBackpackTypeId() or sourceCell.getTypeId() == GetEquipmentTypeId() then
if inventory.unitRemoveItem(sourceItem) then
call audio.searchSoundFile("Sound\\Interface\\HeroDropItem1.wav", 10, .54)
call board.release()
endif
elseif sourceCell.getTypeId() == GetNativeInventoryTypeId() then
call UnitRemoveItem(inventory.source, sourceItem)
call audio.searchSoundFile("Sound\\Interface\\HeroDropItem1.wav", 10, .54)
call board.release()
endif
call inventory.deselect()
else
call inventory.errorMsg("No item selected!")
endif
debug else
debug call ThrowWarning(true, "InventoryControll", "onClick", "action", this, "Undefined operation!")
endif
call audio.bigButtonClick()
set sourceItem = null
endmethod
//* Creates the window. Sizes are equal to the total inventory screen.
private static method onCreate takes Inventory inventory returns thistype
local thistype this = thistype.create(inventory, 0., 0., inventory.width, inventory.height)
local unit source = inventory.source
local real posX
local real posY
//**
//* Background image:
//* =================
//* If required you may change the image type to IMAGE_TYPE_INDICATOR,
//* as the tinting of IMAGE_TYPE_UBERSPLAT images are affected by the time of day.
local ARGB color = Inventory_GetBackgroundColor(source)
local image img = addScreenImage(Inventory_GetBackgroundFilePath(source), 0., 0., width, height, IMAGE_TYPE_UBERSPLAT)
call SetImageColor(img, color.red, color.green, color.blue, color.alpha)
//**
//* Close button:
//* =============
//* Total width - 96 for x and 128 for y are emperial coordinates for the button position.
//* If you want change the model in the user setup or even make it an image button.
set addDestButton(TRACK_64x64, width - 96., 128., 0., 270., Inventory_GetCloseButtonId(source), 1.).data = CLOSE_INVENTORY
//**
//* Scroll backpack up & down:
//* ==========================
//* Both buttons are attached to the lower left corner of the backpack border.
//* There is a small adjustment in x axis ( -32 - UI_BORDER_WIDTH*.5 ) substracted.
//* The buttons have a distance of 24 in y axis towards each others center. ( - 40 and +8 )
if (Inventory_GetBackpackPageCount(source) > 1) then
set posX = width*.5 - 32.*Inventory_GetBackpackColumnCount(source) - 32. - UI_BORDER_WIDTH*.5
set posY = height*.26 - 32.*Inventory_GetBackpackRowCount(source) + 64.
set addImageButton(TRACK_32x32, posX, posY + 8., 0., 270., Inventory_GetScrollUpPath(source), .75).data = SCROLL_UP_BACKPACK
set addImageButton(TRACK_32x32, posX, posY - 40., 0., 270., Inventory_GetScrollDownPath(source), .75).data = SCROLL_DOWN_BACKPACK
endif
//**
//* Drop selection:
//* ===============
//* This button is located at the upper left corner of the backpack.
set posX = width*.5 - 32.*Inventory_GetBackpackColumnCount(inventory.source) - 32. - UI_BORDER_WIDTH*.5
set posY = height*.26 + 8
set addDestButton(TRACK_32x32, posX, posY + 96, 0., 270., Inventory_GetDropButtonId(source), .75).data = DROP_SELECTION
set source = null
return this
endmethod
implement InventoryStruct
endstruct
endlibrary
//TESH.scrollpos=1
//TESH.alwaysfold=0
// Written by BPower.
library InventoryMerchant uses InventoryControll
globals
private constant integer PAWN_ITEM = -1
private constant integer BUY_ITEM = -2
endglobals
struct InventoryMerchant extends UIWindow
private method onHover takes Inventory inventory, UIButton hovered returns nothing
local integer data = hovered.data
if (data == PAWN_ITEM) then
call inventory.createTextBox("Click to pawn the selected item.", hovered.x - 96, hovered.y - 128, 150)
elseif (data == BUY_ITEM) then
call inventory.createTextBox("Click to buy the selected item.", hovered.x - 96, hovered.y - 128, 150)
endif
endmethod
private method onActionButton takes item object, integer action returns nothing
local Inventory inventory = screen
if (action == PAWN_ITEM) then
if (object != null) then
if inventory.unitPawnItem(object, inventory.shop) then
call audio.searchSoundFile("Abilities\\Spells\\Items\\ResourceItems\\ReceiveGold.wav", 10, .54)
call inventory.deselect()
else
call inventory.errorMsg(GetUnitName(inventory.source) + ": I cannot do this!")
endif
else
call inventory.errorMsg("No item selected!")
endif
endif
endmethod
private method onClick takes Inventory inventory, UIButton clicked returns nothing
local integer data = clicked.data
local item object = inventory.selected.getItem()
if (data < 0) then
call onActionButton(object, data)
endif
set object = null
call audio.mouseClick()
endmethod
private static method onCreate takes Inventory inventory returns thistype
local real tile = inventory.tileSize
local real posY = tile*3.5 + Inventory_GetBackpackRowCount(inventory.source)*tile
local thistype this = thistype.create(inventory, tile*3, posY, tile*4, tile*3)
local integer row = 0
local integer column = 0
local string path = Inventory_GetBackpackSlotFilePath(inventory.source)
// Create a 2D grid.
loop
exitwhen (row == 3)
set column = 0
loop
exitwhen (column == 4)
set addImageButton(TRACK_64x64, tile*.5 + column*tile , tile*.5 + row*tile + tile, 0., 270, path, 1).data = 0
set column = column + 1
endloop
set row = row + 1
endloop
if (INVENTORY_MERCHANT_EXTRA_PAGES != 0) and false then
call addPages(INVENTORY_MERCHANT_EXTRA_PAGES, false, false)
endif
call inventory.initInventoryCells(this)
call Inventory_SetBackpackBorderFields(inventory.source)
call border.construct(originX + width*.5, originY + height*.5 + tile, width, height, screen.scale)
set addDestButton(TRACK_64x64, width*.5 - tile, tile*.25, 0., 270., Inventory_GetPawnButtonId(inventory.source), .75).data = PAWN_ITEM
set addDestButton(TRACK_64x64, width*.5 + tile, tile*.25, 0., 270., Inventory_GetBuyButtonId(inventory.source), .75).data = BUY_ITEM
return this
endmethod
implement InventoryStruct
endstruct
function Inventory_ShowShop takes unit source, unit merchant, boolean flag returns boolean
local Inventory dex = GetUnitInventory(source)
if (dex.exists) and (dex.hasWindowOfType(InventoryMerchant.typeid)) then
if not (dex.enabled) and (flag) then
call dex.show(true)
endif
call dex.getWindowOfType(InventoryMerchant.typeid).show(flag)
endif
return true
endfunction
endlibrary
//TESH.scrollpos=15
//TESH.alwaysfold=0
//**
//* Item type charms:
//* =================
//* Charms are items, which add affixes on item picked up event.
//**
//* Fields:
//* =======
//* ItemClass ITEM_CLASS_CHARM
//**
//* Example code:
//* =============
//* call RegisterCustomItemId('I000', ITEM_CLASS_CHARM, false)
//========================================================================
//* InventoryItemTypeCharm code. Make changes carefully.
//========================================================================
library InventoryItemTypeCharm uses Inventory
globals
ItemClass ITEM_CLASS_CHARM
endglobals
private function OnPickupItem takes nothing returns nothing
local item eventItem = Inventory_GetTriggerItem()
local Inventory dex = GetTriggerInventory()
if (IsCustomItemClass(eventItem, ITEM_CLASS_CHARM)) then
call AddUnitItemAffixes(dex.source, eventItem)// Add affixes.
call dex.addFx(eventItem)// Add effects.
endif
set eventItem = null
endfunction
private function OnDropItem takes nothing returns nothing
local item eventItem = Inventory_GetTriggerItem()
local Inventory dex = GetTriggerInventory()
if (IsCustomItemClass(eventItem, ITEM_CLASS_CHARM)) then
call RemoveUnitItemAffixes(dex.source, eventItem)
call dex.removeFx(eventItem)
endif
set eventItem = null
endfunction
private struct I extends array
private static method init takes nothing returns nothing
set ITEM_CLASS_CHARM = ItemClass.create("Charm")
call RegisterInventoryEvent(EVENT_INVENTORY_UNIT_PICKUP_ITEM, function OnPickupItem)
call RegisterInventoryEvent(EVENT_INVENTORY_UNIT_DROP_ITEM, function OnDropItem)
endmethod
implement UIInit
endstruct
endlibrary
//TESH.scrollpos=12
//TESH.alwaysfold=0
//**
//* How to create an inventory for a unit:
//* ======================================
//* function CreateInventory takes unit source, real posX, real posY, integer dummyId, real dummyOffsetY returns Inventory.
scope IntializeUnit initializer Init
//* How to access the Inventory surface is up to you.
//* Most common events are:
//* - on esc key pressed.
//* - on ability event ( maybe in a spellbook )
//* - on item event
//*
//* In this example I detect a certain spell effect event to open or close an Inventory.
globals
private constant integer ABILITY_ID = 'A000'
unit HERO
endglobals
private function OnSpellEffect takes nothing returns nothing
//* Get a unit inventory.
local Inventory inventory = GetUnitInventory(GetTriggerUnit())
//* Is it our ability?
if (GetSpellAbilityId() == ABILITY_ID) then
//* For more safety inventory inherits an "exists" operator from the UIScreen parent struct.
//* It will return false, if this inventory is not allocated.
if (inventory.exists) then
//* inventory.show has the same logic like i.e. ShowUnit(unit, boolean)
//* "inventory.enabled" gives us information if an inventory is currently shown.
//* Show when closed, hide when opened.
call inventory.show(not inventory.enabled)
endif
endif
endfunction
private function AddStuffToUnit takes unit u returns nothing
call UnitAddAbility(u, ABILITY_ID)
call UnitMakeAbilityPermanent(u, true, ABILITY_ID)
endfunction
private function Init takes nothing returns nothing
local unit u2 = CreateUnit(Player(0), 'Ogrh', -1600, 3300, 0)
local unit u = CreateUnit(Player(0), 'Edem', -1600, 3300, 0)
//* unit, originX, originY
call CreateInventory(u, 500., -900)
//* unit, dummyId, offsetY ( offsetY is different for every unit )
call AddUnitInventoryDummy(u, 'h000', 1.5)
set HERO = u
//*
//* Inventory interfaces can be on the same position.
//* Using a dummy unit as portrait is optional. For this one I didn't want to.
call CreateInventory(u2, 500., -900.)
//*
call SetCameraPosition(GetUnitX(u), GetUnitY(u))
call SelectUnit(u, true)
//*
call AddStuffToUnit(u)
call AddStuffToUnit(u2)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function OnSpellEffect)
set u = null
set u2 = null
endfunction
endscope
//TESH.scrollpos=1
//TESH.alwaysfold=0
library InitializeItems initializer Init uses Inventory
////
// How to initialize custom items:
// ===============================
// function RegisterCustomItemId takes integer itemTypeId, ItemClass class
private function Init takes nothing returns nothing
////
// Constant items:
// ===============
//These items have a permanent tooltip, which is never changing.
// Boots of Speed.
call RegisterCustomItemId('I000', ITEM_CLASS_BOOTS)
call SetCustomItemIcon('I000', null, null, 'B003', 0)
call WordWrapStringToItemTooltip('I000', "Increases the movement speed of the hero when worn.", 400., TOOLTIP_POINTER_STORY)
call AddCustomItemIdBonus('I000', BONUS_MOVEMENT_SPEED, 15)
call GenerateItemTooltip('I000')
call UISound.register('I000', "Sound\\Interface\\PickUpItem.wav", 173, 2, 0.2)
// Circle of Nobility.
call RegisterCustomItemId('cnob', ITEM_CLASS_HELMET)
call SetCustomItemIcon('cnob', null, null, 'B007', 0)
call WordWrapStringToItemTooltip('cnob', "Increases the Strength, Agility and Intelligence of the Hero when worn.", 400., TOOLTIP_POINTER_STORY)
call AddCustomItemIdBonus('cnob', BONUS_AGILITY, 2)
call AddCustomItemIdBonus('cnob', BONUS_STRENGTH, 2)
call AddCustomItemIdBonus('cnob', BONUS_INTELLIGENCE, 2)
call GenerateItemTooltip('cnob')
call UISound.register('cnob', "Sound\\Units\\Combat\\MetalLightChopMetal3.wav", 403, 2, 0.2)
// Claw.
call RegisterCustomItemId('I001', ITEM_CLASS_WEAPON)
call SetCustomItemIcon('I001', null, null, 'B000', 0)
// WrapWrap helps us to transform long strings into a tooltip.
call WordWrapStringToItemTooltip('I001', "|cffdaa520The only student to survive the Demon Road returned with her hand cut off. A smoldering claw was sewn in its place.|r", 400. , TOOLTIP_POINTER_STORY)
call GenerateItemTooltip('I001')
call AddCustomItemIdBonus('I001', BONUS_DAMAGE, 11)
call UISound.register('I001', "Sound\\Units\\Combat\\MetalLightChopMetal3.wav", 403, 2, 0.2)
// Heal potion.
call RegisterCustomItemId('phea', ItemClass.ANY)
call SetCustomItemIcon('phea', null, null, 'B001', 0)
call WordWrapStringToItemTooltip('phea', "Heals 150 hit points when used.", 400., TOOLTIP_POINTER_STORY)
call GenerateItemTooltip('phea')
// Potion of clarity.
call RegisterCustomItemId('plcl', ItemClass.ANY)
call SetCustomItemIcon('plcl', null, null, 'B008', 0)
call WordWrapStringToItemTooltip('plcl', "Regenerates mana over time.", 400., TOOLTIP_POINTER_STORY)
call GenerateItemTooltip('plcl')
// Ankh of Reincarnation.
call RegisterCustomItemId('ankh', ItemClass.ANY)
call SetCustomItemIcon('ankh', null, null, 'B002', 0)
call WordWrapStringToItemTooltip('ankh', "Automatically brings the Hero back to life with 500 hit points when the Hero wearing the Ankh dies.", 400., TOOLTIP_POINTER_STORY)
call GenerateItemTooltip('ankh')
// Small Charm.
call RegisterCustomItemId('I003', ITEM_CLASS_CHARM)
call SetCustomItemIcon('I003', null, null, 'B005', 0)
call WordWrapStringToItemTooltip('I003', "Charms unfold their power after beeing picked up.", 400., TOOLTIP_POINTER_STORY)
call GenerateItemTooltip('I003')
call AddCustomItemIdBonus('I003', BONUS_DAMAGE, 4)
// Blue gem.
call RegisterCustomItemId('I005', 0)// Gems and runes are not working yet.
call SetCustomItemIcon('I005', null, null, 'B006', 0)
call WordWrapStringToItemTooltip('I005', "Gems can be socketed into items.", 400., TOOLTIP_POINTER_STORY)
call GenerateItemTooltip('I005')
call AddCustomItemIdBonus('I005', BONUS_STRENGTH, 10)
//
// Dynamic items:
// ==============
// These items roll random affixes.
// Slayer.
call RegisterCustomItemId('I002', ITEM_CLASS_WEAPON)
call SetCustomItemIcon('I002', null, null, 'B004', 0)
call SetCustomItemTwohand('I002', true)
call SetCustomItemIdSockets('I002', 3, 5)
// For item ( 'I002' ) loopindex ( 0 ), min ( 1 ), max ( 2 )
call SetItemIdAffixRollRange('I002', 0, 1, 2)
call SetItemIdAffixRollRange('I002', 1, 1, 1)
call AddItemIdRandomAffix('I002', 0, "Bonus", BONUS_STRENGTH, 2, 9)
call AddItemIdRandomAffix('I002', 0, "Bonus", BONUS_INTELLIGENCE, 5, 8)
call AddItemIdRandomAffix('I002', 0, "Bonus", BONUS_AGILITY, 1, 4)
call AddItemIdRandomAffix('I002', 1, "Ability", 'ACbh', 1, 1)
call AddItemIdRandomAffix('I002', 1, "Ability", 'ACct', 1, 1)
call AddItemIdRandomAffix('I002', 2, "Bonus", BONUS_DAMAGE, 2, 6)
// WrapWrap helps us to transform long strings into a tooltip.
call WordWrapStringToItemTooltip('I002', "|cffdaa520There are longswords and there are greatswords. And then there is this sword.|r", 400. , TOOLTIP_POINTER_STORY)
call GenerateItemTooltip('I002')
call UISound.register('I002', "Sound\\Units\\Combat\\MetalLightChopMetal3.wav", 403, 2, 0.2)
endfunction
endlibrary
//TESH.scrollpos=13
//TESH.alwaysfold=0
scope InitializeEvents initializer Init
private function OnPawn takes nothing returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., "EVENT_INVENTORY_UNIT_PAWN_ITEM")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., " ")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., "Trigger Inventory [" + I2S(GetTriggerInventory()) + "]")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., "Trigger item [" + GetItemName(Inventory_GetTriggerItem()) + "]")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., "Event target unit [" + GetUnitName(Inventory_GetEventTargetUnit()) + "]")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., " ")
endfunction
private function OnDrop takes nothing returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., "EVENT_INVENTORY_UNIT_DROP_ITEM")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., " ")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., "Trigger Inventory [" + I2S(GetTriggerInventory()) + "]")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., "Trigger item [" + GetItemName(Inventory_GetTriggerItem()) + "]")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., "Event target unit [" + GetUnitName(Inventory_GetEventTargetUnit()) + "]")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., " ")
endfunction
private function OnPick takes nothing returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., "EVENT_INVENTORY_UNIT_PICKUP_ITEM")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., " ")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., "Trigger Inventory [" + I2S(GetTriggerInventory()) + "]")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., "Trigger item [" + GetItemName(Inventory_GetTriggerItem()) + "]")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., "Event target unit [" + GetUnitName(Inventory_GetEventTargetUnit()) + "]")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., " ")
endfunction
private function OnUnequip takes nothing returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., "EVENT_INVENTORY_UNIT_UNEQUIP_ITEM")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., " ")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., "Trigger Inventory [" + I2S(GetTriggerInventory()) + "]")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., "Trigger item [" + GetItemName(Inventory_GetTriggerItem()) + "]")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., "Event target unit [" + GetUnitName(Inventory_GetEventTargetUnit()) + "]")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., " ")
endfunction
private function OnEquip takes nothing returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., "EVENT_INVENTORY_UNIT_EQUIP_ITEM")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., " ")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., "Trigger Inventory [" + I2S(GetTriggerInventory()) + "]")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., "Trigger item [" + GetItemName(Inventory_GetTriggerItem()) + "]")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., "Event target unit [" + GetUnitName(Inventory_GetEventTargetUnit()) + "]")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5., " ")
endfunction
private function Init takes nothing returns nothing
call RegisterInventoryEvent(EVENT_INVENTORY_UNIT_PAWN_ITEM, function OnPawn)
call RegisterInventoryEvent(EVENT_INVENTORY_UNIT_DROP_ITEM, function OnDrop)
call RegisterInventoryEvent(EVENT_INVENTORY_UNIT_PICKUP_ITEM, function OnPick)
call RegisterInventoryEvent(EVENT_INVENTORY_UNIT_EQUIP_ITEM, function OnEquip)
call RegisterInventoryEvent(EVENT_INVENTORY_UNIT_UNEQUIP_ITEM, function OnUnequip)
endfunction
endscope
//TESH.scrollpos=50
//TESH.alwaysfold=0
scope InitCustomItemClasses
//**
//* Creating ItemClasses:
//* =====================
//* Item classes must be initialized via ItemClass.create("name")
//* You can create a sub class of another class via myClass.createSubClass("name")
//* Also possible is a sub class of another subclass -> mySubClass.createSubClass("name")
//* Finally it looks like a tree with branches.
//*
//* The root of all classes is 0 which resembles ITEM_CLASS_ANY. Check this:
//* ItemClass.ANY
//* / | \
//* Example: class: WEAPON = ItemClass.create("Weapon") WAND WEAPON BOW
//* subclass: AXE = WEAPON.createSubClass("Axe") | \
//* subclass: MIGHTY_AXE = AXE.createSubClass("Mighty Axe") AXE CROSS_BOW
//* subclass: WARRIOR_AXE = AXE.createSubClass("Warrior Axe"). / \
//* MIGHTY_AXE WARRIOR_AXE
//**
//* Example Setup:
//* ==============
globals
ItemClass ITEM_CLASS_HAND = 0
ItemClass ITEM_CLASS_BOW = 0
ItemClass ITEM_CLASS_QUIVER = 0
ItemClass ITEM_CLASS_WAND = 0
ItemClass ITEM_CLASS_WEAPON = 0
ItemClass ITEM_CLASS_ORB = 0
ItemClass ITEM_CLASS_MIGHTY_WEAPON = 0
ItemClass ITEM_CLASS_FIST_WEAPON = 0
ItemClass ITEM_CLASS_BOOK = 0
ItemClass ITEM_CLASS_POTION = 0
ItemClass ITEM_CLASS_BELT = 0
ItemClass ITEM_CLASS_RING = 0
ItemClass ITEM_CLASS_BOOTS = 0
ItemClass ITEM_CLASS_CHEST = 0
ItemClass ITEM_CLASS_PANTS = 0
ItemClass ITEM_CLASS_AMULET = 0
ItemClass ITEM_CLASS_GLOVES = 0
ItemClass ITEM_CLASS_HELMET = 0
ItemClass ITEM_CLASS_SHOULDER = 0
ItemClass ITEM_CLASS_WRIST = 0
ItemClass ITEM_CLASS_HAT = 0
ItemClass ITEM_CLASS_CORONA = 0
ItemClass ITEM_CLASS_MIGHTY_HELMET = 0
endglobals
private function InitClasses takes nothing returns nothing
//* Main Classes:
//* =============
set ITEM_CLASS_HAND = ItemClass.create("Hand")
set ITEM_CLASS_BOW = ItemClass.create("Bow")
set ITEM_CLASS_QUIVER = ItemClass.create("Quiver")
set ITEM_CLASS_WAND = ItemClass.create("Wand")
//* Hand sub classes - Char classes:
set ITEM_CLASS_WEAPON = ITEM_CLASS_HAND.createSubClass("Weapon")
set ITEM_CLASS_ORB = ITEM_CLASS_HAND.createSubClass("Orb")
//* Offhand sub sub classes.
set ITEM_CLASS_FIST_WEAPON = ITEM_CLASS_WEAPON.createSubClass("Fist Weapon")
set ITEM_CLASS_MIGHTY_WEAPON = ITEM_CLASS_WEAPON.createSubClass("Mighty Weapon")
set ITEM_CLASS_BOOK = ITEM_CLASS_FIST_WEAPON.createSubClass("Book")
//* Potions:
//* ========
set ITEM_CLASS_POTION = ItemClass.create("Potion")
//* Helmet Classes:
//* ===============
set ITEM_CLASS_HELMET = ItemClass.create("Helmet")
set ITEM_CLASS_CORONA = ITEM_CLASS_HELMET.createSubClass("Corona")
set ITEM_CLASS_HAT = ITEM_CLASS_HELMET.createSubClass("Hat")
set ITEM_CLASS_MIGHTY_HELMET = ITEM_CLASS_HELMET.createSubClass("Mighty Helmet")
//* Other Classes:
//* ==============
set ITEM_CLASS_BELT = ItemClass.create("Belt")
set ITEM_CLASS_RING = ItemClass.create("Ring")
set ITEM_CLASS_BOOTS = ItemClass.create("Boots")
set ITEM_CLASS_CHEST = ItemClass.create("Chest")
set ITEM_CLASS_PANTS = ItemClass.create("Pant")
set ITEM_CLASS_AMULET = ItemClass.create("Amulet")
set ITEM_CLASS_GLOVES = ItemClass.create("Glove")
set ITEM_CLASS_WRIST = ItemClass.create("Wrist")
set ITEM_CLASS_SHOULDER = ItemClass.create("Shoulder")
endfunction
private struct I extends array
private static method init takes nothing returns nothing
call InitClasses()
endmethod
implement UIInit
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
library TrebuchetMS uses Font
//**
//* Example: Trebuchet MS:
//* ======================
//* From Microsoft Word. It's best to initialize fonts, like here, via module initializer.
globals
font TREBUCHET_MS
endglobals
//* Next to chars, fonts can contain images. You can give them any name you want.
//* Example: font.getImage("|iorcsymbol|i") will returns this file path.
private function AddImagesToFont takes nothing returns nothing
call TREBUCHET_MS.addImage("orcsymbol", 32, "UI\\Glues\\Loading\\Backgrounds\\Campaigns\\OrcSymbol.blp")
endfunction
private function CreateFontAndAddChars takes nothing returns nothing
//* Create a new font. API is documentated in library Font.
set TREBUCHET_MS=font.create(0)//* no parent font.
call TREBUCHET_MS.addChar(" ", 16, "war3mapImported\\TrebuchetMS32.blp")
call TREBUCHET_MS.addChar("!", 8, "war3mapImported\\TrebuchetMS33.blp")
call TREBUCHET_MS.addChar("\"", 10, "war3mapImported\\TrebuchetMS34.blp")
call TREBUCHET_MS.addChar("#", 19, "war3mapImported\\TrebuchetMS35.blp")
call TREBUCHET_MS.addChar("$", 16, "war3mapImported\\TrebuchetMS36.blp")
call TREBUCHET_MS.addChar("%", 20, "war3mapImported\\TrebuchetMS37.blp")
call TREBUCHET_MS.addChar("&", 22, "war3mapImported\\TrebuchetMS38.blp")
call TREBUCHET_MS.addChar("'", 6, "war3mapImported\\TrebuchetMS39.blp")
call TREBUCHET_MS.addChar("(", 11, "war3mapImported\\TrebuchetMS40.blp")
call TREBUCHET_MS.addChar(")", 10, "war3mapImported\\TrebuchetMS41.blp")
call TREBUCHET_MS.addChar("*", 14, "war3mapImported\\TrebuchetMS42.blp")
call TREBUCHET_MS.addChar("+", 16, "war3mapImported\\TrebuchetMS43.blp")
call TREBUCHET_MS.addChar(",", 9, "war3mapImported\\TrebuchetMS44.blp")
call TREBUCHET_MS.addChar("-", 10, "war3mapImported\\TrebuchetMS45.blp")
call TREBUCHET_MS.addChar(".", 8, "war3mapImported\\TrebuchetMS46.blp")
call TREBUCHET_MS.addChar("/", 15, "war3mapImported\\TrebuchetMS47.blp")
call TREBUCHET_MS.addChar("0", 18, "war3mapImported\\TrebuchetMS48.blp")
call TREBUCHET_MS.addChar("1", 11, "war3mapImported\\TrebuchetMS49.blp")
call TREBUCHET_MS.addChar("2", 18, "war3mapImported\\TrebuchetMS50.blp")
call TREBUCHET_MS.addChar("3", 16, "war3mapImported\\TrebuchetMS51.blp")
call TREBUCHET_MS.addChar("4", 19, "war3mapImported\\TrebuchetMS52.blp")
call TREBUCHET_MS.addChar("5", 16, "war3mapImported\\TrebuchetMS53.blp")
call TREBUCHET_MS.addChar("6", 18, "war3mapImported\\TrebuchetMS54.blp")
call TREBUCHET_MS.addChar("7", 18, "war3mapImported\\TrebuchetMS55.blp")
call TREBUCHET_MS.addChar("8", 18, "war3mapImported\\TrebuchetMS56.blp")
call TREBUCHET_MS.addChar("9", 17, "war3mapImported\\TrebuchetMS57.blp")
call TREBUCHET_MS.addChar(":", 8, "war3mapImported\\TrebuchetMS58.blp")
call TREBUCHET_MS.addChar(";", 9, "war3mapImported\\TrebuchetMS59.blp")
call TREBUCHET_MS.addChar("<", 15, "war3mapImported\\TrebuchetMS60.blp")
call TREBUCHET_MS.addChar("=", 17, "war3mapImported\\TrebuchetMS61.blp")
call TREBUCHET_MS.addChar(">", 15, "war3mapImported\\TrebuchetMS62.blp")
call TREBUCHET_MS.addChar("?", 13, "war3mapImported\\TrebuchetMS63.blp")
call TREBUCHET_MS.addChar("@", 24, "war3mapImported\\TrebuchetMS64.blp")
call TREBUCHET_MS.addChar("A", 22, "war3mapImported\\TrebuchetMS65.blp")
call TREBUCHET_MS.addChar("B", 18, "war3mapImported\\TrebuchetMS66.blp")
call TREBUCHET_MS.addChar("C", 20, "war3mapImported\\TrebuchetMS67.blp")
call TREBUCHET_MS.addChar("D", 19, "war3mapImported\\TrebuchetMS68.blp")
call TREBUCHET_MS.addChar("E", 17, "war3mapImported\\TrebuchetMS69.blp")
call TREBUCHET_MS.addChar("F", 18, "war3mapImported\\TrebuchetMS70.blp")
call TREBUCHET_MS.addChar("G", 21, "war3mapImported\\TrebuchetMS71.blp")
call TREBUCHET_MS.addChar("H", 20, "war3mapImported\\TrebuchetMS72.blp")
call TREBUCHET_MS.addChar("I", 7, "war3mapImported\\TrebuchetMS73.blp")
call TREBUCHET_MS.addChar("J", 15, "war3mapImported\\TrebuchetMS74.blp")
call TREBUCHET_MS.addChar("K", 20, "war3mapImported\\TrebuchetMS75.blp")
call TREBUCHET_MS.addChar("L", 17, "war3mapImported\\TrebuchetMS76.blp")
call TREBUCHET_MS.addChar("M", 26, "war3mapImported\\TrebuchetMS77.blp")
call TREBUCHET_MS.addChar("N", 19, "war3mapImported\\TrebuchetMS78.blp")
call TREBUCHET_MS.addChar("O", 22, "war3mapImported\\TrebuchetMS79.blp")
call TREBUCHET_MS.addChar("P", 18, "war3mapImported\\TrebuchetMS80.blp")
call TREBUCHET_MS.addChar("Q", 26, "war3mapImported\\TrebuchetMS81.blp")
call TREBUCHET_MS.addChar("R", 19, "war3mapImported\\TrebuchetMS82.blp")
call TREBUCHET_MS.addChar("S", 16, "war3mapImported\\TrebuchetMS83.blp")
call TREBUCHET_MS.addChar("T", 21, "war3mapImported\\TrebuchetMS84.blp")
call TREBUCHET_MS.addChar("U", 19, "war3mapImported\\TrebuchetMS85.blp")
call TREBUCHET_MS.addChar("V", 21, "war3mapImported\\TrebuchetMS86.blp")
call TREBUCHET_MS.addChar("W", 29, "war3mapImported\\TrebuchetMS87.blp")
call TREBUCHET_MS.addChar("X", 20, "war3mapImported\\TrebuchetMS88.blp")
call TREBUCHET_MS.addChar("Y", 21, "war3mapImported\\TrebuchetMS89.blp")
call TREBUCHET_MS.addChar("Z", 18, "war3mapImported\\TrebuchetMS90.blp")
call TREBUCHET_MS.addChar("[", 11, "war3mapImported\\TrebuchetMS91.blp")
call TREBUCHET_MS.addChar("\\", 14, "war3mapImported\\TrebuchetMS92.blp")
call TREBUCHET_MS.addChar("]", 11, "war3mapImported\\TrebuchetMS93.blp")
call TREBUCHET_MS.addChar("^", 15, "war3mapImported\\TrebuchetMS94.blp")
call TREBUCHET_MS.addChar("_", 20, "war3mapImported\\TrebuchetMS95.blp")
call TREBUCHET_MS.addChar("`", 9, "war3mapImported\\TrebuchetMS96.blp")
call TREBUCHET_MS.addChar("a", 16, "war3mapImported\\TrebuchetMS97.blp")
call TREBUCHET_MS.addChar("b", 13, "war3mapImported\\TrebuchetMS98.blp")
call TREBUCHET_MS.addChar("c", 15, "war3mapImported\\TrebuchetMS99.blp")
call TREBUCHET_MS.addChar("d", 14, "war3mapImported\\TrebuchetMS100.blp")
call TREBUCHET_MS.addChar("e", 12, "war3mapImported\\TrebuchetMS101.blp")
call TREBUCHET_MS.addChar("f", 13, "war3mapImported\\TrebuchetMS102.blp")
call TREBUCHET_MS.addChar("g", 16, "war3mapImported\\TrebuchetMS103.blp")
call TREBUCHET_MS.addChar("h", 14, "war3mapImported\\TrebuchetMS104.blp")
call TREBUCHET_MS.addChar("i", 5, "war3mapImported\\TrebuchetMS105.blp")
call TREBUCHET_MS.addChar("j", 11, "war3mapImported\\TrebuchetMS106.blp")
call TREBUCHET_MS.addChar("k", 15, "war3mapImported\\TrebuchetMS107.blp")
call TREBUCHET_MS.addChar("l", 13, "war3mapImported\\TrebuchetMS108.blp")
call TREBUCHET_MS.addChar("m", 19, "war3mapImported\\TrebuchetMS109.blp")
call TREBUCHET_MS.addChar("n", 14, "war3mapImported\\TrebuchetMS110.blp")
call TREBUCHET_MS.addChar("o", 16, "war3mapImported\\TrebuchetMS111.blp")
call TREBUCHET_MS.addChar("p", 14, "war3mapImported\\TrebuchetMS112.blp")
call TREBUCHET_MS.addChar("q", 18, "war3mapImported\\TrebuchetMS113.blp")
call TREBUCHET_MS.addChar("r", 14, "war3mapImported\\TrebuchetMS114.blp")
call TREBUCHET_MS.addChar("s", 12, "war3mapImported\\TrebuchetMS115.blp")
call TREBUCHET_MS.addChar("t", 16, "war3mapImported\\TrebuchetMS116.blp")
call TREBUCHET_MS.addChar("u", 14, "war3mapImported\\TrebuchetMS117.blp")
call TREBUCHET_MS.addChar("v", 16, "war3mapImported\\TrebuchetMS118.blp")
call TREBUCHET_MS.addChar("w", 21, "war3mapImported\\TrebuchetMS119.blp")
call TREBUCHET_MS.addChar("x", 15, "war3mapImported\\TrebuchetMS120.blp")
call TREBUCHET_MS.addChar("y", 16, "war3mapImported\\TrebuchetMS121.blp")
call TREBUCHET_MS.addChar("z", 13, "war3mapImported\\TrebuchetMS122.blp")
call TREBUCHET_MS.addChar("{", 14, "war3mapImported\\TrebuchetMS123.blp")
call TREBUCHET_MS.addChar("|", 6, "war3mapImported\\TrebuchetMS124.blp")
call TREBUCHET_MS.addChar("}", 14, "war3mapImported\\TrebuchetMS125.blp")
call TREBUCHET_MS.addChar("~", 15, "war3mapImported\\TrebuchetMS126.blp")
endfunction
private struct I extends array
private static method init takes nothing returns nothing
call CreateFontAndAddChars()
call AddImagesToFont()
endmethod
implement UIInit
endstruct
endlibrary
//TESH.scrollpos=38
//TESH.alwaysfold=0
library InventoryItemSockets uses CustomItem
//**
//* Item sockets:
//* =============
//* Sockets are slots in items, which can be filled with gems.
globals
private constant real CHANCE_TO_ROLL_SOCKETS = 100//* From 1 to 100.
endglobals
//**
//* API:
//* ====
//! novjass
//* Rolls between min and max. min must be > 0 and max is capped with 6.
function SetCustomItemIdSockets takes integer itemId, integer min, integer max returns nothing
//* amount is capped with 6. For 0 the sockets will be removed.
function SetCustomItemSockets takes item whichItem integer amount returns nothing
//! endnovjass
//**
//* Fields:
//* =======
//* ItemClass ITEM_CLASS_GEM
//**
//* Example code:
//* =============
//* call RegisterCustomItemIdSockets('I000', 1, 3)
//========================================================================
//* InventoryItemTypeCharm code. Make changes carefully.
//========================================================================
globals
ItemClass ITEM_CLASS_GEM
endglobals
function SetCustomItemSockets takes item whichItem, integer amount returns nothing
endfunction
private struct ItemSockets
private Table table
private method updateTooltip takes integer object returns nothing
local integer dex2
local integer max2
local integer id
local integer pos = 1
local integer dex = 0
local integer max = table[-2]
call FlushItemTooltipPointer(object, TOOLTIP_POINTER_SOCKETS)
call SetItemTooltipFragment(object, 0, "Sockets "+ I2S(max) + "/" + I2S(getSockets()), null, TOOLTIP_POINTER_SOCKETS)
loop
exitwhen dex == max
set id = table[dex]
set dex2 = 0
set max2 = GetItemTooltipSize(id)
loop
exitwhen dex2 == max2
if (GetItemTooltipFragmentPointer(id ,dex2) == TOOLTIP_POINTER_AFFIXES) then
call BJDebugMsg("found affixes")
call SetItemTooltipFragment(object, pos, GetItemTooltipFragment(id, dex2), GetItemTooltipFragmentIcon(id, dex2), TOOLTIP_POINTER_SOCKETS)
set pos = pos + 1
endif
set dex2 = dex2 + 1
endloop
set dex = dex + 1
endloop
call GenerateItemTooltip(object)
endmethod
static method operator [] takes item whichItem returns thistype
return thistype(0).table[GetHandleId(whichItem)]
endmethod
method getSockets takes nothing returns integer
return table[-1]
endmethod
method getFreeSockets takes nothing returns integer
return table[-1] - table[-2]
endmethod
method setSockets takes integer amount returns nothing
set table[-1] = amount
endmethod
method socket takes item toInsert returns boolean
local integer id = GetHandleId(toInsert)
local integer dex = table[-2]
if (getFreeSockets() > 0) then
set table[-2] = dex + 1
set table[dex] = id
call BJDebugMsg("added " + I2S(id) + " to item on slot " + I2S(dex))
call updateTooltip(thistype(0).table[this])
return true
endif
return false
endmethod
static method create takes integer id returns thistype
local thistype this = thistype.allocate()
set table = Table.create()
set thistype(0).table[id] = this
set thistype(0).table[this] = id
return this
endmethod
private static method onCreateItem takes nothing returns nothing
local CustomItem object = GetEventCustomItem()
local integer roll = IMinBJ(GetCustomItemIdSockets(object.getItemId()), 6)
local thistype this
if (roll <= 0) or (CHANCE_TO_ROLL_SOCKETS < GetRandomReal(1., 100.)) then
return
endif
set this = create(object)
call setSockets(roll)
call updateTooltip(object)
endmethod
static method onDestroyItem takes nothing returns nothing
local integer id = GetEventCustomItem()
local thistype this = thistype(0).table[id]
if (this != 0) then
call deallocate()
call table.destroy()
call thistype(0).table.remove(id)
call thistype(0).table.remove(this)
endif
endmethod
private static method init takes nothing returns nothing
set thistype(0).table = Table.create()
set ITEM_CLASS_GEM = ItemClass.create("Gem")
call RegisterCustomItemEvent(EVENT_CUSTOM_ITEM_DESTROY, function thistype.onDestroyItem)
call RegisterCustomItemEvent(EVENT_CUSTOM_ITEM_CREATE, function thistype.onCreateItem)
endmethod
implement UIInit
endstruct
function SocketCustomItemWithGem takes item whichItem, item whichGem returns boolean
return ItemSockets[whichItem].socket(whichGem)
endfunction
function GetCustomItemFreeSockets takes item whichItem returns integer
local ItemSockets s = ItemSockets[whichItem]
return s.getFreeSockets()
endfunction
endlibrary
//TESH.scrollpos=234
//TESH.alwaysfold=0
library AutoIndex
//===========================================================================
// Information:
//==============
//
// AutoIndex is a very simple script to utilize. Just call GetUnitId(unit) to
// get get the unique value assigned to a particular unit. AutoIndex differs from
// other unit indexing libraries because it automatically assigns an ID to each
// unit as it enters the map, and automatically frees that ID as the unit leaves
// the map. This gives you several advantages as the user:
//
// 1.) The GetUnitId function inlines directly to a GetUnitUserData call (or a
// LoadInteger call if UseUnitUserData is disabled.)
// 2.) You don't need to manually free IDs as units leave the map.
// 3.) Detecting removing units to free their indexes is O(1), and less costly
// performance-wise than a timer scanning the map for removed units.
//
// If you turn on debug mode, AutoIndex will become slower, but it will show
// you a variety of helpful error messages. It can detect the following problems:
// -Passing a null unit to GetUnitId
// -Passing a removed or decayed unit to GetUnitId
// -Code outside of AutoIndex has overwritten a unit's UserData value.
// -GetUnitId was used on a filtered unit (a unit you don't want indexed).
//
// AutoIndex also provides events upon indexing or deindexing units. This
// effectively allows you to notice when units enter or leave the game, and
// handle the creation or destruction of attached data or other things.
//
//===========================================================================
// How to install AutoIndex:
//===========================
//
// 1.) Copy and paste this script into your map.
// 2.) Save it to allow the ObjectMerger macro to generate the "Leave Detect"
// ability for you. Close and re-open the map. After that, disable the macro
// to prevent the save delay.
//
//===========================================================================
// How to use AutoIndex:
//=======================
//
// So you can get a unique integer for each unit, but how do you use that to
// attach data to a unit? GetUnitId will always return a number in the range of
// 1-8190. This means it can be used as an array index, as demonstrated below:
//
// globals
// integer array IntegerData
// real array RealData
// SomeStruct array SomeStructData
// englobals
//
// function Example takes nothing returns nothing
// local unit u = CreateUnit(...)
// local integer id = GetUnitId(u)
// //You now have a unique index for the unit, so you can
// //attach or retrieve data about the unit using arrays.
// set IntegerData[id] = 5
// set RealData[id] = 25.0
// set SomeStructData[id] = SomeStruct.create()
// //If you have access to the same unit in another function, you can
// //retrieve the data by using GetUnitId() and reading the arrays.
// endfunction
//
// The UnitFilter function in the config section is provided so that you can
// make AutoIndex completely ignore any unit-types that don't want to be indexed.
// You may want to ignore dummy casters or system-private units, especially ones
// that use UnitUserData internally. You don't need to worry about xe dummy units,
// as those are automatically filtered.
//
//===========================================================================
// How to use OnUnitIndexed / OnUnitDeindexed:
//=============================================
//
// AutoIndex will fire the OnUnitIndexed event when a unit enters the map,
// and the OnUnitDeindexed event when a unit leaves the map. Functions used
// as events must take a unit and return nothing. An example is given below:
//
// function UnitEntersMap takes unit u returns nothing
// call BJDebugMsg(GetUnitName(u)+" was indexed with the ID "+I2S(GetUnitId(u)))
// endfunction
//
// function UnitLeavesMap takes unit u returns nothing
// call BJDebugMsg(GetUnitName(u)+" was deindexed with the ID "+I2S(GetUnitId(u)))
// endfunction
//
// function Init takes nothing returns nothing
// call OnUnitIndexed(UnitEntersMap)
// call OnUnitDeindexed(UnitLeavesMap)
// endfunction
//
// As you can see, it works perfectly fine to call GetUnitId() on a unit
// during either of these events.
//
// If you call OnUnitIndexed during map initialization, every existing
// unit will be considered as entering the map. This saves you from needing
// to manually enumerate preplaced units (or units created by initialization
// code that ran before OnUnitIndexed was called).
//
// OnUnitDeindexed runs while a unit still exists, which means you can
// still do things such as destroy special effects attached to the unit.
// The unit will cease to exist immediately after the event is over.
//
//===========================================================================
// AutoIndex API:
//================
//
// GetUnitId(unit) -> integer
// This function returns a unique ID in the range of 1-8190 for the
// specified unit. Use it to attach data to the unit. This function
// inlines directly to GetUnitUserData or LoadInteger if debug mode
// is disabled. If debug mode is enabled, it can display error mess-
// ages when passed a null, decayed or filtered unit.
//
// IsUnitIndexed(unit) -> boolean
// This function returns a boolean indicating whether the specified
// unit is indexed or not. A unit would not be indexed if you ignored
// it using the UnitFilter function, or if it is a xe dummy unit.
//
// OnUnitIndexed(IndexFunc)
// This function accepts an IndexFunc, which must take a unit and
// return nothing. The IndexFunc will be fired instantly whenever
// a unit enters the map. You may use GetUnitId on the unit. When
// you call this function during map initialization, every existing
// unit will be considered as entering the map.
//
// OnUnitDeindexed(IndexFunc)
// Same as above, but runs whenever a unit is leaving the map. When
// this event runs, the unit still exists, but it will cease to exist
// as soon as the event ends. You may use GetUnitId on the unit.
//
//===========================================================================
// Configuration:
//================
//! external ObjectMerger w3a Adef lvdt anam "Leave Detect" aart "" arac 0
//Save your map with this Object Merger call enabled, then close and reopen your
//map. Disable it by removing the exclamation to remove the delay while saving.
globals
private constant integer LeaveDetectAbilityID = 'lvdt'
//This rawcode must match the parameter after "Adef" in the
//ObjectMergermacro above. You can change both if you want.
private constant boolean UseUnitUserData = true
//If this is set to true, UnitUserData will be used. You should only set
//this to false if something else in your map already uses UnitUserData.
//A hashtable will be used instead, but it is about 60% slower.
endglobals
public function UnitFilter takes nothing returns boolean
return true
endfunction
//Any units you filter out in this function will not be indexed.
//Use GetFilterUnit() to refer to the filtered unit. You do not
//need to filter out xe dummy units; they are already filtered.
//===========================================================================
// User functions:
//=================
function GetUnitId takes unit u returns integer
static if DEBUG_MODE then
return AutoIndex.getIndexDebug(u)
else
return AutoIndex.getIndex(u)
endif
endfunction
function IsUnitIndexed takes unit u returns boolean
return AutoIndex.isUnitIndexed(u)
endfunction
function interface IndexFunc takes unit u returns nothing
function OnUnitIndexed takes IndexFunc func returns nothing
call AutoIndex.onUnitIndexed(func)
endfunction
function OnUnitDeindexed takes IndexFunc func returns nothing
call AutoIndex.onUnitDeindexed(func)
endfunction
//===========================================================================
hook RemoveUnit AutoIndex.hook_RemoveUnit
hook ReplaceUnitBJ AutoIndex.hook_ReplaceUnitBJ
debug hook SetUnitUserData AutoIndex.hook_SetUnitUserData
struct AutoIndex
private static trigger enter = CreateTrigger()
private static trigger status = CreateTrigger()
private static trigger creepdeath = CreateTrigger()
private static group preplaced = CreateGroup()
private static timer allowdecay = CreateTimer()
private static hashtable ht
private static boolean array dead
private static boolean array summoned
private static boolean array animated
private static boolean array nodecay
private static boolean array removing
private static IndexFunc array indexfuncs
private static integer indexfuncs_n = -1
private static IndexFunc array deindexfuncs
private static integer deindexfuncs_n = -1
private static IndexFunc indexfunc
private static unit array allowdecayunit
private static integer allowdecay_n = -1
private static boolean duringinit = true
private static boolean array altered
private static unit array idunit
//===========================================================================
static method getIndex takes unit u returns integer
static if UseUnitUserData then
return GetUnitUserData(u)
else
return LoadInteger(ht, 0, GetHandleId(u))
endif
endmethod
//Resolves to an inlinable one-liner after the static if.
static method getIndexDebug takes unit u returns integer
local integer index = getIndex(u)
if u == null then
call BJDebugMsg("AutoIndex error: Null unit passed to GetUnitId.")
elseif GetUnitTypeId(u) == 0 then
call BJDebugMsg("AutoIndex error: Removed or decayed unit passed to GetUnitId.")
elseif idunit[index] != u then
call BJDebugMsg("AutoIndex error: "+GetUnitName(u)+" is a filtered unit.")
endif
return index
endmethod
//If debug mode is enabled, use the getIndex method that shows errors.
static method setIndex takes unit u, integer index returns nothing
static if UseUnitUserData then
call SetUnitUserData(u, index)
else
call SaveInteger(ht, 0, GetHandleId(u), index)
endif
endmethod
//Resolves to an inlinable one-liner after the static if.
//===========================================================================
static method isUnitAnimateDead takes unit u returns boolean
return animated[getIndex(u)]
endmethod
//Don't use this; use IsUnitAnimateDead from StatusEvents instead.
static method isUnitIndexed takes unit u returns boolean
return u != null and idunit[getIndex(u)] == u
endmethod
//===========================================================================
private static method onUnitIndexed_sub takes nothing returns nothing
call indexfunc.evaluate(GetEnumUnit())
endmethod
//During initialization, evaluate the indexfunc for every preplaced unit.
static method onUnitIndexed takes IndexFunc func returns nothing
set indexfuncs_n = indexfuncs_n + 1
set indexfuncs[indexfuncs_n] = func
if duringinit then
set indexfunc = func
call ForGroup(preplaced, function AutoIndex.onUnitIndexed_sub)
endif
endmethod
static method onUnitDeindexed takes IndexFunc func returns nothing
set deindexfuncs_n = deindexfuncs_n + 1
set deindexfuncs[deindexfuncs_n] = func
endmethod
//===========================================================================
private static method hook_RemoveUnit takes unit whichUnit returns nothing
set removing[getIndex(whichUnit)] = true
endmethod
private static method hook_ReplaceUnitBJ takes unit whichUnit, integer newUnitId, integer unitStateMethod returns nothing
set removing[getIndex(whichUnit)] = true
endmethod
//Intercepts whenever RemoveUnit or ReplaceUnitBJ is called and sets a flag.
private static method hook_SetUnitUserData takes unit whichUnit, integer data returns nothing
static if UseUnitUserData then
if IsUnitIndexed(whichUnit) then
if getIndex(whichUnit) == data then
call BJDebugMsg("AutoIndex error: Code outside AutoIndex attempted to alter "+GetUnitName(whichUnit)+"'s index.")
else
call BJDebugMsg("AutoIndex error: Code outside AutoIndex altered "+GetUnitName(whichUnit)+"'s index.")
if idunit[data] != null then
call BJDebugMsg("AutoIndex error: "+GetUnitName(whichUnit)+" and "+GetUnitName(idunit[data])+" now have the same index.")
endif
set altered[data] = true
endif
endif
endif
endmethod
//In debug mode, intercepts whenever SetUnitUserData is used on an indexed unit.
//Displays an error message if outside code tries to alter a unit's index.
//===========================================================================
private static method allowDecay takes nothing returns nothing
local integer n = allowdecay_n
loop
exitwhen n < 0
set nodecay[getIndex(allowdecayunit[n])] = false
set allowdecayunit[n] = null
set n = n - 1
endloop
set allowdecay_n = -1
endmethod
//Iterate through all the units in the stack and allow them to decay again.
private static method detectStatus takes nothing returns boolean
local unit u = GetTriggerUnit()
local integer index = getIndex(u)
local integer n
if idunit[index] == u then //Ignore non-indexed units.
if not IsUnitType(u, UNIT_TYPE_DEAD) then
if dead[index] then //The unit was dead, but now it's alive.
set dead[index] = false //The unit has been resurrected.
//! runtextmacro optional RunStatusEvent("Resurrect")
//If StatusEvents is in the map, run the resurrection events.
if IsUnitType(u, UNIT_TYPE_SUMMONED) and not summoned[index] then
set summoned[index] = true //If the unit gained the summoned flag,
set animated[index] = true //it's been raised with Animate Dead.
//! runtextmacro optional RunStatusEvent("AnimateDead")
//If StatusEvents is in the map, run the Animate Dead events.
endif
endif
else
if not removing[index] and not dead[index] and not animated[index] then
set dead[index] = true //The unit was alive, but now it's dead.
set nodecay[index] = true //A dead unit can't decay for at least 0. seconds.
set allowdecay_n = allowdecay_n + 1 //Add the unit to a stack. After the timer
set allowdecayunit[allowdecay_n] = u //expires, allow the unit to decay again.
call TimerStart(allowdecay, 0., false, function AutoIndex.allowDecay)
//! runtextmacro optional RunStatusEvent("Death")
//If StatusEvents is in the map, run the Death events.
//! runtextmacro optional TransportUnload()
//If TransportEvents is in the map, remove the dead unit from whatever transport it's in.
elseif removing[index] or (dead[index] and not nodecay[index]) or (not dead[index] and animated[index]) then
//If .nodecay was false and the unit is dead and was previously dead, the unit decayed.
//If .animated was true and the unit is dead, the unit died and exploded.
//If .removing was true, the unit is being removed or replaced.
//! runtextmacro optional TransportUnload()
//If TransportEvents is in the map, remove the leaving unit from whatever transport it's in.
set n = deindexfuncs_n
loop //Run the OnUnitDeindexed events.
exitwhen n < 0
call deindexfuncs[n].evaluate(u)
set n = n - 1
endloop
//! runtextmacro optional TransportClean()
//If TransportEvents is in the map, and the leaving unit is a
//transport, clean the transport- related data from the unit.
call AutoIndex(index).destroy() //Free the index by destroying the AutoIndex struct.
set idunit[index] = null //Null this unit reference to prevent a leak.
endif
endif
endif
set u = null
return false
endmethod
private static method isUndefendOrder takes nothing returns boolean
//! runtextmacro optional TransportUnloadCheck()
//If TransportEvents is in the map, check whether a unit is unloading.
return GetIssuedOrderId() == 852056
endmethod
//===========================================================================
private static method unitEntersMap takes unit u returns nothing
local integer index
local integer n = 0
if getIndex(u) != 0 then //If a unit already has an ID, don't assign a new one.
return //This only happens if a unit leaves the entire map area.
endif
set index = create()
call setIndex(u, index) //Assign an index to the entering unit.
call UnitAddAbility(u, LeaveDetectAbilityID) //Add the leave detect ability to the entering unit.
call UnitMakeAbilityPermanent(u, true, LeaveDetectAbilityID) //Prevent it from disappearing on morph.
set dead[index] = IsUnitType(u, UNIT_TYPE_DEAD) //Reset all of the flags for the entering
set summoned[index] = IsUnitType(u, UNIT_TYPE_SUMMONED) //unit. These flags are necessary to detect
set animated[index] = false //when the unit leaves the map.
set nodecay[index] = false
set removing[index] = false
debug set altered[index] = false //In debug mode, this flag tracks wheter a unit's index was altered.
set idunit[index] = u //Attach the unit that is supposed to have this index to the index.
loop //Run the OnUnitIndexed events.
exitwhen n > indexfuncs_n
call indexfuncs[n].evaluate(u)
set n = n + 1
endloop
endmethod
private static method initPreplacedUnit takes nothing returns nothing
static if LIBRARY_xebasic then
if GetUnitTypeId(GetEnumUnit()) == XE_DUMMY_UNITID then
return //Don't index xe dummy units, and don't add
endif //them to the group of preplaced units.
endif
call GroupAddUnit(preplaced, GetEnumUnit()) //Assemble a group of all the preplaced units.
call unitEntersMap(GetEnumUnit()) //Initialize each preplaced unit.
return
endmethod
private static method initEnteringUnit takes nothing returns boolean
static if LIBRARY_xebasic then
if GetUnitTypeId(GetFilterUnit()) == XE_DUMMY_UNITID then
return false //Don't index xe dummy units, and don't add
endif //them to the group of preplaced units.
endif
if duringinit then
call GroupAddUnit(preplaced, GetFilterUnit())
//Add units that are created during initialization to the preplaced units group.
//This ensures that all units are noticed by OnUnitIndexed during initialization.
endif
call unitEntersMap(GetFilterUnit()) //Initialize each unit that enters the map.
return false
endmethod
//===========================================================================
private static method afterInit takes nothing returns nothing
set duringinit = false //Initialization is over; set a flag.
call DestroyTimer(GetExpiredTimer()) //Destroy the timer.
call GroupClear(preplaced) //The preplaced units group is
call DestroyGroup(preplaced) //no longer needed, so clean it.
set preplaced = null
endmethod
private static method onInit takes nothing returns nothing
local region maparea = CreateRegion()
local rect bounds = GetWorldBounds()
local group g = CreateGroup()
local integer i = 15
static if not UseUnitUserData then
set ht = InitHashtable() //Only create a hashtable if it will be used.
endif
loop
exitwhen i < 0
call SetPlayerAbilityAvailable(Player(i), LeaveDetectAbilityID, false)
//Make the LeaveDetect ability unavailable so that it doesn't show up on the command card of every unit.
call TriggerRegisterPlayerUnitEvent(status, Player(i), EVENT_PLAYER_UNIT_ISSUED_ORDER, function UnitFilter)
//Register the "EVENT_PLAYER_UNIT_ISSUED_ORDER" event to notice Undefend orders. Ignore filtered units.
call GroupEnumUnitsOfPlayer(g, Player(i), Condition(function UnitFilter))
call ForGroup(g, function AutoIndex.initPreplacedUnit)
//Enum every non-filtered unit on the map during initialization and assign it a unique
//index. By using GroupEnumUnitsOfPlayer, even units with Locust can be detected.
set i = i - 1
endloop
call TriggerAddCondition(status, And(function AutoIndex.isUndefendOrder, function AutoIndex.detectStatus))
//The detectStatus method will fire every time a non-filtered unit recieves an undefend order.
//And() is used here to avoid using a trigger action, which starts a new thread and is slower.
call TriggerRegisterPlayerUnitEvent(creepdeath, Player(12), EVENT_PLAYER_UNIT_DEATH, function UnitFilter)
call TriggerAddCondition(creepdeath, function AutoIndex.detectStatus)
//The detectStatus method must also fire when a neutral hostile creep dies, in case it was
//sleeping. Sleeping creeps don't fire undefend orders on non-damaging deaths.
call RegionAddRect(maparea, bounds) //GetWorldBounds() contains the entire map area, including the shaded boundry areas.
call TriggerRegisterEnterRegion(enter, maparea, And(function UnitFilter, function AutoIndex.initEnteringUnit))
//Only the filter function of an EnterRegion trigger runs instantly when a unit is created.
//Using And() lets both the UnitFilter and indexing function run when a unit enters the map.
call TimerStart(CreateTimer(), 0., false, function AutoIndex.afterInit)
//After any time elapses, perform after-initialization actions.
call GroupClear(g)
call DestroyGroup(g)
call RemoveRect(bounds)
set g = null
set bounds = null
endmethod
endstruct
endlibrary
//TESH.scrollpos=232
//TESH.alwaysfold=0
library BonusMod initializer OnInit requires optional AbilityPreload, optional xepreload
private keyword AbilityBonus
////////////////////////////////////////////////////////////////////////////////
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//@ BonusMod - v3.3.1
//@=============================================================================
//@ Credits:
//@-----------------------------------------------------------------------------
//@ Written by:
//@ Earth-Fury
//@ Based on the work of:
//@ weaaddar
//@-----------------------------------------------------------------------------
//@ If you use this system, please at least credit weaaddar. Without him, this
//@ system would not exist. I would be happy if you credited me as well.
//@=============================================================================
//@ Requirements:
//@-----------------------------------------------------------------------------
//@ This library is written in vJass and thus requires JASS Helper in order to
//@ function correctly. This library also uses the ObjectMerger created by
//@ PitzerMike. The ObjectMerger must be configured as an external tool for
//@ JASS Helper.
//@
//@ All of these things are present in the NewGen world editor.
//@
//@=============================================================================
//@ Introduction:
//@-----------------------------------------------------------------------------
//@ BonusMod is a system for applying reversible bonuses to certain stats, such
//@ as attack speed or mana regen, for specific units. Most of the bonuses
//@ provided by BonusMod show green or red numbers in the command card, exactly
//@ like the bonuses provided by items.
//@
//@ BonusMod has two kinds of bonuses:
//@ 1. Ability based bonuses
//@ 2. Code based bonuses
//@
//@ All of the bonuses in the configuration section for the basic BonusMod
//@ library are ability-based bonuses. Code-based bonuses are provided by
//@ additional libraries.
//@
//@ Ability based bonuses have a limit to how much of a bonus they can apply.
//@ The actual limit depends on the number of abilities that type of bonus uses.
//@ See the "Default bonuses" section of this readme for the default limits
//@ of the bonuses that come with BonusMod. For changing the limits of the
//@ default bonuses, or for adding new types of bonuses, see the below
//@ configuration section.
//@
//@ Code based bonuses may or may not have a limit to how much of a bonus they
//@ can apply. The limits for code based bonuses depend entirely on how the
//@ bonus is implemented. See their documentation for more information.
//@
//@=============================================================================
//@ Adding BonusMod to your map:
//@-----------------------------------------------------------------------------
//@ First, you must place the BonusMod library in a custom-text trigger in your
//@ map.
//@
//@ You must then save your map with ability generation enabled. After you save
//@ your map with ability generation enabled, you must close your map in the
//@ editor, and reopen it. You can then disable ability generation.
//@ See the configuration section for information on how to enable and disable
//@ ability generation.
//@
//@=============================================================================
//@ Default bonuses:
//@-----------------------------------------------------------------------------
//@
//@ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//@ | Bonus Type constants: | Minimum bonus: | Maximum bonus: |
//@ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//@ | BONUS_SIGHT_RANGE | -2048 | +2047 |
//@ | BONUS_ATTACK_SPEED | -512 | +511 |
//@ | BONUS_ARMOR | -1024 | +1023 |
//@ | BONUS_MANA_REGEN_PERCENT | -512% | +511% |
//@ | BONUS_LIFE_REGEN | -256 | +255 |
//@ | BONUS_DAMAGE | -1024 | +1023 |
//@ | BONUS_STRENGTH | -256 | +255 |
//@ | BONUS_AGILITY | -256 | +255 |
//@ | BONUS_INTELLIGENCE | -256 | +255 |
//@ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//@
//@ Notes:
//@ - The bonuses for stength, agility, and intelligence can only be
//@ applied to heroes. Attempting to add them to normal units will
//@ fail to work completely.
//@ - Using a negative BONUS_STRENGTH bonus can give a unit negative
//@ maximum life. Don't do that. It really messes stuff up.
//@ - Using a negative BONUS_INTELLIGENCE bonus can remove a hero's
//@ mana. This is not a big issue, as mana will return when the
//@ bonus is removed.
//@ - The maximum effective sight range for a unit is 1800.
//@ - There is a maximum attack speed. I have no idea what it is.
//@
//@ See the configuration section for information on how to change the range of
//@ bonuses, as well as how to add new ability-based bonuses, and remove unused
//@ ones.
//@
//@=============================================================================
//@ Public API / Function list:
//@-----------------------------------------------------------------------------
//@ Note that BonusMod will only output error messages if JASS Helper is set to
//@ compile in debug mode.
//@
//@ Bonus constants such as BONUS_DAMAGE have .min and .max properties which
//@ are the minimum and maximum bonus that type of bonus can apply. Note that
//@ for code based bonuses, these constants may not reflect the minimum or
//@ maximum bonus for a specific unit. Use the IsBonusValid() function to check
//@ if the given bonus value is okay for a given unit.
//@
//@ function SetUnitBonus
//@ takes unit u, Bonus bonusType, integer amount
//@ returns integer
//@
//@ This function sets the bonus of the type bonusType for the given unit to
//@ the given amount. The returned integer is the unit's actual current
//@ bonus, after it has been changed. If the given amount is above the
//@ maximum possible bonus for this type, then the maximum possible bonus
//@ is applied to the unit. The same is true if the given value is below
//@ the minimum possible bonus.
//@
//@ function GetUnitBonus
//@ takes unit u, Bonus bonusType
//@ returns integer
//@
//@ Returns the given unit's current bonus of bonusType. A value of 0 means
//@ that the given unit does not have a bonus of the given type.
//@
//@ function AddUnitBonus
//@ takes unit u, Bonus bonusType, integer amount
//@ returns integer
//@
//@ Increases the unit's bonus by the given amount. You can use a negitive
//@ amount to subtract from the unit's current bonus. Note that the same
//@ rules SetUnitBonus has apply for going over/under the maximum bonus.
//@ The returned value is the unit's actual new bonus.
//@
//@ function RemoveUnitBonus
//@ takes unit u, Bonus bonusType
//@ returns nothing
//@
//@ Sets the bonus of the type bonusType to 0 for the given unit. This
//@ function is faster then using SetUnitBonus(u, bonusType, 0).
//@
//@ function IsBonusValid
//@ takes unit u, Bonus abstractBonus, integer value
//@ returns boolean
//@
//@ Returns true if the given value is a valid bonus value for the given
//@ unit. This will also return false if the given bonus type is a hero-
//@ only bonus type, and the given unit is not a hero.
//@
//@=============================================================================
//@ Writing code-based bonuses:
//@-----------------------------------------------------------------------------
//@ This section of the readme tells you how to create your own bonus types
//@ that apply their bonuses using vJass code instead of abilities. You do not
//@ need to read or understand this to use BonusMod as-is.
//@
//@ Creating a new bonus type is simple. Extend the Bonus struct, implement the
//@ methods provided within it, and create a single instance of your struct
//@ within a variable named BONUS_YOUR_BONUS_TYPES_NAME of the type Bonus.
//@
//@ The methods you must implement are:
//@
//@ method setBonus takes unit u, integer amount returns integer
//@ This method sets the given unit's current bonus to amount, returning
//@ the actual bonus that was applied. If the given amount is higher then
//@ the maximum amount your bonus type can apply to a unit, you must apply
//@ the maximum possible bonus, and return that amount. The same holds true
//@ for the minimum bonus.
//@
//@ method getBonus takes unit u returns integer
//@ This method returns the current bonus the given unit has.
//@
//@ method removeBonus takes unit u returns nothing
//@ This method sets the current bonus of the given unit to 0.
//@
//@ method isValueInRange takes integer value returns boolean
//@ This method returns true if the given integer is a valid bonus amount
//@ for this bonus type, and false otherwise.
//@
//@ Note that it is your responsibility to do any clean up in the event a unit
//@ dies or is removed with an active bonus on it. There is no guarantee that
//@ removeBonus() will be called before a unit dies or is removed.
//@
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
////////////////////////////////////////////////////////////////////////////////
//==============================================================================
// Configuration:
//==============================================================================
//------------------------------------------------------------------------------
// If the following constant is set to true, the abilities used by this library
// will be preloaded at map initialization. This will slightly increase loading
// time, but will prevent a slight to medium lag spike the first time a bonus
// of a type is applied.
//
// Note that your map must contain either the xepreload library, or the
// AbilityPreload library for preloading to work.
//
// It is highly recommended that you do not set this to false.
//------------------------------------------------------------------------------
globals
private constant boolean PRELOAD_ABILITIES = false
endglobals
//------------------------------------------------------------------------------
// The BonusMod_BeginBonuses macro takes a single boolean type parameter.
// If set to true, bonus abilities will be created (or recreated) on save.
// If set to false, abilities will not be generated.
//
// If you modify any of the bonus declaration macros, or add new ones, you must
// regenerate abilities.
//
// Note that if you remove a bonus, the abilities it had created will not be
// automatically removed. This is also true of reducing the number of abilities
// a bonus uses.
//
// After you generate abilities, you must close your map and reopen it in the
// editor. You can then disable ability generation until the next time you
// modify the bonus types.
//------------------------------------------------------------------------------
//! runtextmacro BonusMod_BeginBonuses("false")
//--------------------------------------------------------------------------
// Below are where bonus types are defined.
//
// The first parameter is the name of the bonus type. A constant will be
// generated for each bonus type, that will take the form: BONUS_NAME
//
// The second parameter is the maximum power of 2 the bonus type can add
// to a unit. For example, 8 abilities gives a range of -256 to +255.
//
// The third parameter is the base ability. The base ability must give the
// desired effect when the given field is changed.
//
// The fourth parameter is the rawcode prefix of the bonuses generated
// abilities. The prefix must be 3 characters long. Your map must not
// already contain bonuses which start with the given prefix.
//
// The fifth parameter is the object field to modify for each generated
// ability.
//
// The sixth parameter must be true of the bonus should only work on hero
// units, and false otherwise.
//
// The final parameter is the icon that will be displayed in the object
// editor. This has no effect on anything ingame.
//--------------------------------------------------------------------------
// | NAME |ABILITY|SOURCE |PREFIX|FIELD | HERO | ICON
// | | COUNT |ABILITY| | | ONLY |
//! runtextmacro BonusMod_DeclareBonus("ARMOR", "10", "AId1", "(A)", "Idef", "false", "BTNHumanArmorUpOne.blp")
//! runtextmacro BonusMod_DeclareBonus("DAMAGE", "10", "AItg", "(B)", "Iatt", "false", "BTNSteelMelee.blp")
//! runtextmacro BonusMod_DeclareBonus("SIGHT_RANGE", "11", "AIsi", "(C)", "Isib", "false", "BTNTelescope.blp")
//! runtextmacro BonusMod_DeclareBonus("LIFE_REGEN", "8", "Arel", "(E)", "Ihpr", "false", "BTNRingSkull.blp")
//! runtextmacro BonusMod_DeclareBonus("STRENGTH", "8", "AIa1", "(F)", "Istr", "true" , "BTNGoldRing.blp")
//! runtextmacro BonusMod_DeclareBonus("AGILITY", "8", "AIa1", "(G)", "Iagi", "true" , "BTNGoldRing.blp")
//! runtextmacro BonusMod_DeclareBonus("INTELLIGENCE", "8", "AIa1", "(H)", "Iint", "true" , "BTNGoldRing.blp")
// | NAME |ABILITY|SOURCE |PREFIX|FIELD |HERO | ICON
// | | COUNT |ABILITY| | | ONLY |
//! runtextmacro BonusMod_DeclarePercentBonus("ATTACK_SPEED", "9", "AIsx", "(I)", "Isx1", "false", "BTNGlove.blp")
//! runtextmacro BonusMod_DeclarePercentBonus("MANA_REGEN_PERCENT", "9", "AIrm", "(D)", "Imrp", "false", "BTNSobiMask.blp")
//! runtextmacro BonusMod_EndBonuses()
//==============================================================================
// End of configuration
//==============================================================================
//! textmacro BonusMod_BeginBonuses takes SHOULD_GENERATE_ABILITIES
private function Setup takes nothing returns nothing
// The following is a lua script for the ObjectMerger, used to generate abilities
//*
//! externalblock extension=lua ObjectMerger $FILENAME$
//! i if "$SHOULD_GENERATE_ABILITIES$" == "true" then
//! i function FormatName(name)
//! i name = string.lower(name)
//! i name = string.gsub(name, "_", " ")
//! i s = name
//! i name = ""
//! i for w in string.gmatch(s, "%a%w*") do
//! i name = name .. string.upper(string.sub(w, 1, 1)) .. string.sub(w, 2, -1)
//! i name = name .. " "
//! i end
//! i name = string.sub(name, 1, string.len(name) - 1)
//! i return name
//! i end
//! i function SetupAbility(name, suffix, icon, hero)
//! i makechange(current, "anam", "BonusMod - " .. FormatName(name))
//! i makechange(current, "ansf", "(" .. suffix .. ")")
//! i makechange(current, "aart", "ReplaceableTextures\\CommandButtons\\" .. icon)
//! i makechange(current, "aite", 0)
//! i if hero then
//! i makechange(current, "Iagi", 1, 0)
//! i makechange(current, "Iint", 1, 0)
//! i makechange(current, "Istr", 1, 0)
//! i end
//! i end
//! i function CreateAbility(sourceAbility, prefix, field, abilityCount, name, icon)
//! i powOf2 = abilityCount - 1
//! i lengthOfMax = string.len(tostring(2^abilityCount))
//! i for i = 0, powOf2 do
//! i padding = ""
//! i for k = 0, lengthOfMax - string.len(tostring(2^i)) - 1 do
//! i padding = padding .. "0"
//! i end
//! i createobject(sourceAbility, prefix .. string.sub(chars, i + 1, i + 1))
//! i SetupAbility(name, "+" .. padding .. tostring(2 ^ i), icon, true)
//! i makechange(current, field, 1, tostring(2^i))
//! i end
//! i createobject(sourceAbility, prefix .. "-")
//! i SetupAbility(name, "-" .. tostring(2 ^ abilityCount), icon, true)
//! i makechange(current, field, 1, tostring(-(2^abilityCount)))
//! i end
//! i function CreatePercentageAbility(sourceAbility, prefix, field, abilityCount, name, icon)
//! i powOf2 = abilityCount - 1
//! i lengthOfMax = string.len(tostring(2^abilityCount))
//! i for i = 0, powOf2 do
//! i padding = ""
//! i for k = 0, lengthOfMax - string.len(tostring(2^i)) - 1 do
//! i padding = padding .. "0"
//! i end
//! i createobject(sourceAbility, prefix .. string.sub(chars, i + 1, i + 1))
//! i SetupAbility(name, "+" .. padding .. tostring(2 ^ i) .. "%", icon, false)
//! i makechange(current, field, 1, tostring((2 ^ i) / 100))
//! i end
//! i createobject(sourceAbility, prefix .. "-")
//! i SetupAbility(name, "-" .. tostring(2 ^ abilityCount) .. "%", icon, false)
//! i makechange(current, field, 1, tostring(-((2 ^ abilityCount) / 100)))
//! i end
//! i setobjecttype("abilities")
//! i chars = "abcdefghijklmnopqrstuvwxyz"
//! i
// */
//! endtextmacro
//! textmacro BonusMod_DeclareBonus takes NAME, ABILITY_COUNT, SOURCE_ABILITY, RAWCODE_PREFIX, FIELD, HERO_ONLY, ICON
//! i CreateAbility("$SOURCE_ABILITY$", "$RAWCODE_PREFIX$", "$FIELD$", $ABILITY_COUNT$, "$NAME$", "$ICON$")
globals
Bonus BONUS_$NAME$
endglobals
set BONUS_$NAME$ = AbilityBonus.create('$RAWCODE_PREFIX$a', $ABILITY_COUNT$, '$RAWCODE_PREFIX$-', $HERO_ONLY$)
//! endtextmacro
//! textmacro BonusMod_DeclarePercentBonus takes NAME, ABILITY_COUNT, SOURCE_ABILITY, RAWCODE_PREFIX, FIELD, HERO_ONLY, ICON
//! i CreatePercentageAbility("$SOURCE_ABILITY$", "$RAWCODE_PREFIX$", "$FIELD$", $ABILITY_COUNT$, "$NAME$", "$ICON$")
globals
Bonus BONUS_$NAME$
endglobals
set BONUS_$NAME$ = AbilityBonus.create('$RAWCODE_PREFIX$a', $ABILITY_COUNT$, '$RAWCODE_PREFIX$-', $HERO_ONLY$)
//! endtextmacro
//! textmacro BonusMod_EndBonuses
//*
//! i end
//! endexternalblock
// */
endfunction
//! endtextmacro
// ===
// Precomputed integer powers of 2
// ===
globals
private integer array powersOf2
private integer powersOf2Count = 0
endglobals
// ===
// Utility functions
// ===
private function ErrorMsg takes string func, string s returns nothing
call BJDebugMsg("|cffFF0000BonusMod Error|r|cffFFFF00:|r |cff8080FF" + func + "|r|cffFFFF00:|r " + s)
endfunction
private function LoadAbility takes integer abilityId returns nothing
static if PRELOAD_ABILITIES then
static if LIBRARY_xepreload then
call XE_PreloadAbility(abilityId)
else
static if LIBRARY_AbilityPreload then
call AbilityPreload(abilityId)
endif
endif
endif
endfunction
// ===
// Bonus Types
// ===
private interface BonusInterface
integer minBonus = 0
integer maxBonus = 0
private method destroy takes nothing returns nothing defaults nothing
endinterface
private keyword isBonusObject
struct Bonus extends BonusInterface
boolean isBonusObject = false
public static method create takes nothing returns thistype
local thistype this = thistype.allocate()
set this.isBonusObject = true
return this
endmethod
stub method setBonus takes unit u, integer amount returns integer
debug call ErrorMsg("Bonus.setBonus()", "I have no idea how or why you did this, but don't do it.")
return 0
endmethod
stub method getBonus takes unit u returns integer
debug call ErrorMsg("Bonus.getBonus()", "I have no idea how or why you did this, but don't do it.")
return 0
endmethod
stub method removeBonus takes unit u returns nothing
call this.setBonus(u, 0)
endmethod
stub method isValidBonus takes unit u, integer value returns boolean
return true
endmethod
method operator min takes nothing returns integer
return this.minBonus
endmethod
method operator max takes nothing returns integer
return this.maxBonus
endmethod
endstruct
private struct AbilityBonus extends Bonus
public integer count
public integer rawcode
public integer negativeRawcode
public integer minBonus = 0
public integer maxBonus = 0
public boolean heroesOnly
public static method create takes integer rawcode, integer count, integer negativeRawcode, boolean heroesOnly returns thistype
local thistype bonus = thistype.allocate()
local integer i
debug local boolean error = false
// Error messages
static if DEBUG_MODE then
if rawcode == 0 then
call ErrorMsg("AbilityBonus.create()", "Bonus constructed with a rawcode of 0?!")
call bonus.destroy()
return 0
endif
if count < 0 or count == 0 then
call ErrorMsg("AbilityBonus.create()", "Bonus constructed with an ability count <= 0?!")
call bonus.destroy()
return 0
endif
endif
// Grow powers of 2
if powersOf2Count < count then
set i = powersOf2Count
loop
exitwhen i > count
set powersOf2[i] = 2 * powersOf2[i - 1]
set i = i + 1
endloop
set powersOf2Count = count
endif
// Preload this bonus' abilities
static if PRELOAD_ABILITIES then
set i = 0
loop
exitwhen i == count
call LoadAbility(rawcode + i)
set i = i + 1
endloop
if negativeRawcode != 0 then
call LoadAbility(negativeRawcode)
endif
endif
// Set up this bonus object
set bonus.count = count
set bonus.negativeRawcode = negativeRawcode
set bonus.rawcode = rawcode
set bonus.heroesOnly = heroesOnly
// Calculate the minimum and maximum bonuses
if negativeRawcode != 0 then
set bonus.minBonus = -powersOf2[count]
else
set bonus.minBonus = 0
endif
set bonus.maxBonus = powersOf2[count] - 1
// Return the bonus object
return bonus
endmethod
// Interface methods:
method setBonus takes unit u, integer amount returns integer
return SetUnitBonus.evaluate(u, this, amount)
endmethod
method getBonus takes unit u returns integer
return GetUnitBonus.evaluate(u, this)
endmethod
method removeBonus takes unit u returns nothing
call RemoveUnitBonus.evaluate(u, this)
endmethod
public method isValidBonus takes unit u, integer value returns boolean
return (value >= this.minBonus) and (value <= this.maxBonus)
endmethod
endstruct
// ===
// Public API
// ===
function IsBonusValid takes unit u, Bonus abstractBonus, integer value returns boolean
local AbilityBonus bonus = AbilityBonus(abstractBonus)
static if DEBUG_MODE then
if not abstractBonus.isBonusObject then
call ErrorMsg("IsBonusValid()", "Invalid bonus type given")
endif
endif
if abstractBonus.min > value or abstractBonus.max < value then
return false
endif
if abstractBonus.getType() != AbilityBonus.typeid then
return abstractBonus.isValidBonus(u, value)
endif
if bonus.heroesOnly and not IsUnitType(u, UNIT_TYPE_HERO) then
return false
endif
return (value >= bonus.minBonus) and (value <= bonus.maxBonus)
endfunction
function RemoveUnitBonus takes unit u, Bonus abstractBonus returns nothing
local integer i = 0
local AbilityBonus bonus = AbilityBonus(abstractBonus)
static if DEBUG_MODE then
if not abstractBonus.isBonusObject then
call ErrorMsg("RemoveUnitBonus()", "Invalid bonus type given")
endif
endif
if abstractBonus.getType() != AbilityBonus.typeid then
call abstractBonus.removeBonus(u)
return
endif
if bonus.heroesOnly and not IsUnitType(u, UNIT_TYPE_HERO) then
debug call ErrorMsg("RemoveUnitBonus()", "Trying to remove a hero-only bonus from a non-hero unit")
return
endif
call UnitRemoveAbility(u, bonus.negativeRawcode)
loop
exitwhen i == bonus.count
call UnitRemoveAbility(u, bonus.rawcode + i)
set i = i + 1
endloop
endfunction
function SetUnitBonus takes unit u, Bonus abstractBonus, integer amount returns integer
local integer i
local integer output = 0
local AbilityBonus bonus = AbilityBonus(abstractBonus)
local boolean applyMinBonus = false
static if DEBUG_MODE then
if not abstractBonus.isBonusObject then
call ErrorMsg("SetUnitBonus()", "Invalid bonus type given")
endif
endif
if amount == 0 then
call RemoveUnitBonus(u, bonus)
return 0
endif
if abstractBonus.getType() != AbilityBonus.typeid then
return abstractBonus.setBonus(u, amount)
endif
if bonus.heroesOnly and not IsUnitType(u, UNIT_TYPE_HERO) then
debug call ErrorMsg("SetUnitBonus()", "Trying to set a hero-only bonus on a non-hero unit")
return 0
endif
if amount < bonus.minBonus then
debug call ErrorMsg("SetUnitBonus()", "Attempting to set a bonus to below its min value")
set amount = bonus.minBonus
elseif amount > bonus.maxBonus then
debug call ErrorMsg("SetUnitBonus()", "Attempting to set a bonus to above its max value")
set amount = bonus.maxBonus
endif
if amount < 0 then
set amount = -(bonus.minBonus - amount)
set applyMinBonus = true
endif
call UnitRemoveAbility(u, bonus.negativeRawcode)
set i = bonus.count - 1
loop
exitwhen i < 0
if amount >= powersOf2[i] then
call UnitAddAbility(u, bonus.rawcode + i)
call UnitMakeAbilityPermanent(u, true, bonus.rawcode + i)
static if DEBUG_MODE then
if GetUnitAbilityLevel(u, bonus.rawcode + i) <= 0 then
call ErrorMsg("SetUnitBonus()", "Failed to give the 2^" + I2S(i) + " ability to the unit!")
endif
endif
set amount = amount - powersOf2[i]
set output = output + powersOf2[i]
else
call UnitRemoveAbility(u, bonus.rawcode + i)
static if DEBUG_MODE then
if GetUnitAbilityLevel(u, bonus.rawcode + i) > 0 then
call ErrorMsg("SetUnitBonus()", "Unit still has the 2^" + I2S(i) + " ability after it was removed!")
endif
endif
endif
set i = i - 1
endloop
if applyMinBonus then
call UnitAddAbility(u, bonus.negativeRawcode)
call UnitMakeAbilityPermanent(u, true, bonus.negativeRawcode)
else
call UnitRemoveAbility(u, bonus.negativeRawcode)
endif
return output
endfunction
function GetUnitBonus takes unit u, Bonus abstractBonus returns integer
local integer i = 0
local integer amount = 0
local AbilityBonus bonus = AbilityBonus(abstractBonus)
static if DEBUG_MODE then
if not abstractBonus.isBonusObject then
call ErrorMsg("GetUnitBonus()", "Invalid bonus type given")
endif
endif
if abstractBonus.getType() != AbilityBonus.typeid then
return abstractBonus.getBonus(u)
endif
if bonus.heroesOnly and not IsUnitType(u, UNIT_TYPE_HERO) then
debug call ErrorMsg("GetUnitBonus()", "Trying to get a hero-only bonus from a non-hero unit")
return 0
endif
if GetUnitAbilityLevel(u, bonus.negativeRawcode) > 0 then
set amount = bonus.minBonus
endif
loop
exitwhen i == bonus.count
if GetUnitAbilityLevel(u, bonus.rawcode + i) > 0 then
set amount = amount + powersOf2[i]
endif
set i = i + 1
endloop
return amount
endfunction
function AddUnitBonus takes unit u, Bonus bonus, integer amount returns integer
return SetUnitBonus(u, bonus, GetUnitBonus(u, bonus) + amount)
endfunction
// ===
// Initialization
// ===
private function OnInit takes nothing returns nothing
local integer i
// Set up powers of 2
set powersOf2[0] = 1
set powersOf2Count = 1
static if DEBUG_MODE and PRELOAD_ABILITIES and not LIBRARY_xepreload and not LIBRARY_AbilityPreload then
call ErrorMsg("Initialization", "PRELOAD_ABILITIES is set to true, but neither usable preloading library is detected")
endif
// Setup bonuses
call Setup()
endfunction
endlibrary
//TESH.scrollpos=87
//TESH.alwaysfold=0
library UnitMaxState initializer Initialize requires optional AbilityPreload, optional xepreload
//==============================================================================
// UnitMaxState v2.1
//==============================================================================
// Credits:
//------------------------------------------------------------------------------
// Written By:
// Earth-Fury
//
// Original System By:
// Blade.dk
//
// Intermittent Version By:
// Deaod
//
// With Thanks To:
// - weaaddar for BonusMod and thus inspiration
// - PitzerMike for the ObjectMerger
// - Vexorian for vJass and Jass Helper
// - PipeDream for Grimoire
// - SFilip for TESH
// - MindWorX for maintaining NewGen
//------------------------------------------------------------------------------
// If you use this library in your map, please at least give credit to Blade.dk.
// Without him, this library would not exist.
//==============================================================================
// Introduction:
//------------------------------------------------------------------------------
// UnitMaxState is a library which allows you to modify a unit's maximum life,
// or maximum mana. To achieve this, the library abuses a bug with the AIlf and
// AImz abilities, which is too complex to explain here.
//
// I do believe it was indeed Blade.dk who initially found this bug. If not,
// it's still his system I stole and rewrote. Further, let me thank Deaod for
// writing up his version of this system, which inspired both the reality of
// this rewrite, and the method abilities are handled within it.
//
//==============================================================================
// Requirements:
//------------------------------------------------------------------------------
// UnitMaxState is written in vJass and requires the NewGen editor, or
// Jass Helper with PitzerMike's Object Merger configured for it.
//
// UnitMaxState requires the latest version of Jass Helper.
//
// Preloading of abilities requires either AbilityPreload or xepreload. Neither
// are required for the library to function; however, having one or the other
// will remove the slight delay the first time a unit's max state is changed.
//
//==============================================================================
// Using UnitMaxState:
//------------------------------------------------------------------------------
// UnitMaxState comes with two useful functions:
//
// nothing SetUnitMaxState(unit <target>, unitstate <state>, real <value>)
// Changes <target>'s unitstate <state> to be equal to <value>.
//
// nothing AddUnitMaxState(unit <target>, unitstate <state>, real <value>)
// Adds <value> to <target>'s <state> unitstate. Note that you can use
// negative values with this function.
//
// Both of these functions accept unitstate's other than UNIT_STATE_MAX_LIFE and
// UNIT_STATE_MAX_MANA. There is a small performance penalty in using these over
// direct usage of SetUnitState.
//
// You must not use life/mana boosting upgrades in combination with this system.
//
// Attempting to set a unit's maximum life below 1, or mana below 0 will do
// nothing. A debug message will be output if the script is compiled in
// debug mode.
//==============================================================================
//==============================================================================
// Configuration:
//------------------------------------------------------------------------------
// The below textmacro call is an all-in-one configuration line.
//------------------------------------------------------------------------------
// The first parameter is a boolean.
//
// If true, the abilities used by this system will be created on save. This adds
// a slight delay to saving your map. You only ever have to create the abilities
// the first time this library is added to your map, or if you modify any of
// the other configuration options.
//
// Note that to make the ability creation permanent, you must save with ability
// creation enabled, close your map, and reopen it in the editor. You can then
// disable ability creation, as the abilities will be permanently in your map.
//------------------------------------------------------------------------------
// The second parameter is an integer.
//
// This is the number of abilities this system will use for adding/removing
// life/mana. Note that this system uses four sets of abilities, so the actual
// number of abilities generated and used will be the value you pass here,
// multiplied by 4.
//
// The higher this number, the faster large bonuses will be added. This number
// should never have to go above 13. Between 3 and 5 will work fine for most
// maps.
//------------------------------------------------------------------------------
// The fourth and fifth parameters are 3 character prefixes for rawcodes.
//
// The first one is for the life-modifying abilities, while the second is for
// the mana-modifying abilities.
//
// Please, make sure your map has no abilities whose rawcodes begin with either
// of these prefixes before saving! Otherwise, those abilities will be
// overwritten. You can change these to any 3 character combination, if your
// map does already contain abilities whose rawcodes begin with these prefixes.
//------------------------------------------------------------------------------
//! runtextmacro UnitMaxState_Configuration("false", "3", "ZxL", "ZxM")
//------------------------------------------------------------------------------
// End of configuration
//------------------------------------------------------------------------------
//! textmacro UnitMaxState_Configuration takes LOAD_ABILITIES, ABILITY_COUNT, LIFE_PREFIX, MANA_PREFIX
//*
//! externalblock extension=lua ObjectMerger $FILENAME$
//! i function CreateAbilities(baseAbility, rawcodePrefix, field, name, icon)
//! i k = 0
//! i for sign = -1, 1, 2 do
//! i signStr = "+"
//! i if sign < 0 then
//! i signStr = "-"
//! i end
//! i j = 0
//! i for i = 0, (abilityCount - 1) * 3, 3 do
//! i j = j + 1
//! i createobject(baseAbility, rawcodePrefix .. string.sub(Chars, k + 1, k + 1))
//! i makechange(current, "anam", "UnitMaxState - " .. name)
//! i makechange(current, "ansf", "(" .. signStr .. tostring(j) .. ")")
//! i makechange(current, "aart", "ReplaceableTextures\\CommandButtons\\" .. icon)
//! i makechange(current, "aite", 0)
//! i makechange(current, "alev", 4)
//! i makechange(current, field, 1, 0)
//! i makechange(current, field, 2, 2^(i + 0) * sign)
//! i makechange(current, field, 3, 2^(i + 1) * sign)
//! i makechange(current, field, 4, 2^(i + 2) * sign)
//! i k = k + 1
//! i end
//! i end
//! i end
//! i if $LOAD_ABILITIES$ then
//! i setobjecttype("abilities")
//! i abilityCount = $ABILITY_COUNT$
//! i Chars = "abcdefghijklmnopqrstuvwxyz"
//! i CreateAbilities("AIlf", "$LIFE_PREFIX$", "Ilif", "Life", "BTNHealthStone.blp")
//! i CreateAbilities("AImz", "$MANA_PREFIX$", "Iman", "Mana", "BTNManaStone.blp")
//! i end
//! endexternalblock
// */
globals
private constant integer RAWCODE_LIFE = '$LIFE_PREFIX$a'
private constant integer RAWCODE_MANA = '$MANA_PREFIX$a'
public constant integer ABILITY_COUNT = $ABILITY_COUNT$
endglobals
//! endtextmacro
globals
private constant boolean PRELOAD_ABILITIES = false
private integer array POWERS_OF_2
endglobals
private function ErrorMsg takes string s returns nothing
debug call BJDebugMsg("SetUnitMaxState: " + s)
endfunction
function SetUnitMaxState takes unit target, unitstate state, real targetValue returns nothing
local integer difference
local integer rawcode
local integer abilityId
local integer abilityLevel
local integer currentAbility
if state == UNIT_STATE_MAX_LIFE then
set rawcode = RAWCODE_LIFE
if targetValue < 1 then
call ErrorMsg("You can not set a unit's max life to below 1")
return
endif
elseif state == UNIT_STATE_MAX_MANA then
set rawcode = RAWCODE_MANA
if targetValue < 0 then
call ErrorMsg("You can not set a unit's max mana to below 0")
return
endif
else
call SetUnitState(target, state, targetValue)
return
endif
set difference = R2I(targetValue) - R2I(GetUnitState(target, state))
if difference < 0 then
set difference = -difference
set rawcode = rawcode + ABILITY_COUNT
endif
set abilityId = ABILITY_COUNT - 1
set abilityLevel = 4
set currentAbility = rawcode + abilityId
loop
exitwhen difference == 0
if difference >= POWERS_OF_2[abilityId * 3 + (abilityLevel - 2)] then
call UnitAddAbility(target, currentAbility)
call SetUnitAbilityLevel(target, currentAbility, abilityLevel)
call UnitRemoveAbility(target, currentAbility)
set difference = difference - POWERS_OF_2[abilityId * 3 + (abilityLevel - 2)]
else
set abilityLevel = abilityLevel - 1
if abilityLevel <= 1 then
set abilityId = abilityId - 1
set abilityLevel = 4
set currentAbility = rawcode + abilityId
endif
endif
endloop
endfunction
function AddUnitMaxState takes unit target, unitstate state, real additionalValue returns nothing
call SetUnitMaxState(target, state, GetUnitState(target, state) + additionalValue)
endfunction
//! textmacro UnitMaxState_Preload takes RAWCODE
set i = 0
loop
exitwhen i == ABILITY_COUNT * 2 - 1
static if LIBRARY_AbilityPreload then
call AbilityPreload($RAWCODE$ + i)
elseif LIBRARY_xepreload then
call XE_PreloadAbility($RAWCODE$ + i)
endif
set i = i + 1
endloop
//! endtextmacro
private function Initialize takes nothing returns nothing
local integer i
local integer k
set i = 1
set POWERS_OF_2[0] = 1
loop
exitwhen i == ABILITY_COUNT * 2 * 2 * 3 + 1
set POWERS_OF_2[i] = POWERS_OF_2[i - 1] * 2
set i = i + 1
endloop
static if DEBUG_MODE and PRELOAD_ABILITIES and not LIBRARY_AbilityPreload and not LIBRARY_xepreload then
call ErrorMsg("Ability preloading was enabled, but neither of the supported preload libraries are present")
elseif PRELOAD_ABILITIES then
//! runtextmacro UnitMaxState_Preload("RAWCODE_LIFE")
//! runtextmacro UnitMaxState_Preload("RAWCODE_MANA")
endif
endfunction
endlibrary
//TESH.scrollpos=33
//TESH.alwaysfold=0
library RegenBonuses initializer OnInit requires BonusMod, AutoIndex
////////////////////////////////////////////////////////////////////////////////
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//@ RegenBonuses for BonusMod
//@=============================================================================
//@ Credits:
//@-----------------------------------------------------------------------------
//@ Written by:
//@ Earth-Fury
//@=============================================================================
//@ Requirements:
//@-----------------------------------------------------------------------------
//@ This library requires the BonusMod library, and the AutoIndex library.
//@
//@=============================================================================
//@ Readme:
//@-----------------------------------------------------------------------------
//@ This library provides two new bonus types:
//@
//@ - BONUS_MANA_REGEN
//@ Regenerates an absolute amount of mana every second
//@
//@ - BONUS_LIFE_REGEN_PERCENT
//@ Regenerates a percent of a unit's maximum life every second
//@
//@ Any value is a valid bonus. There are no limits.
//@
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
////////////////////////////////////////////////////////////////////////////////
//==============================================================================
// Configuration:
//==============================================================================
//------------------------------------------------------------------------------
// The following constant determines how often the life and mana bonuses are
// applied to units. Note that this will not effect the values of the bonuses.
// The values of the bonuses will always be mana per second/percentage of life
// per second. This constant simply determines how often the values are updated.
//------------------------------------------------------------------------------
globals
private real REGEN_PERIOD = 0.1
endglobals
//==============================================================================
// End of configuration
//==============================================================================
globals
Bonus BONUS_MANA_REGEN
Bonus BONUS_LIFE_REGEN_PERCENT
endglobals
private function ErrorMsg takes string func, string s returns nothing
call BJDebugMsg("|cffFF0000RegenBonuses Error|r|cffFFFF00:|r |cff8080FF" + func + "|r|cffFFFF00:|r " + s)
endfunction
globals
private timer regenTimer = CreateTimer()
endglobals
private keyword BonusValues
private function DoRegen takes nothing returns nothing
local integer i
local BonusValues values
// Loop over all active bonuses
set i = 0
loop
exitwhen i == BonusValues.count
set values = BonusValues.all[i]
if GetWidgetLife(values.owner) > 0.0 then
if values.manaRegen > 0 then
// Do mana regen:
call SetUnitState(values.owner, UNIT_STATE_MANA, GetUnitState(values.owner, UNIT_STATE_MANA) + (values.manaRegen * REGEN_PERIOD))
endif
if values.lifeRegen > 0 then
// Do life regen:
call SetUnitState(values.owner, UNIT_STATE_LIFE, GetUnitState(values.owner, UNIT_STATE_LIFE) + (GetUnitState(values.owner, UNIT_STATE_MAX_LIFE) * (values.lifeRegen / 100.0)) * REGEN_PERIOD)
endif
endif
set i = i + 1
endloop
endfunction
private struct BonusValues
public integer manaRegen = 0
public integer lifeRegen = 0
public unit owner
private static thistype array owners
public static thistype array all
public static integer count = 0
public integer index
private static method create takes unit u returns thistype
local thistype this = thistype.allocate()
set this.owner = u
set thistype.owners[GetUnitId(u)] = this
if thistype.count == 0 then
call TimerStart(regenTimer, REGEN_PERIOD, true, function DoRegen)
endif
set thistype.all[thistype.count] = this
set this.index = thistype.count
set thistype.count = thistype.count + 1
return this
endmethod
private method onDestroy takes nothing returns nothing
set thistype.owners[GetUnitId(this.owner)] = thistype(0)
set thistype.all[this.index] = thistype.all[thistype.count]
set thistype.count = thistype.count - 1
if thistype.count == 0 then
call PauseTimer(regenTimer)
endif
set this.owner = null
endmethod
public static method getInstance takes unit u returns thistype
if thistype.owners[GetUnitId(u)] == 0 then
return thistype.create(u)
else
return thistype.owners[GetUnitId(u)]
endif
endmethod
public static method operator[] takes unit u returns thistype
return thistype.owners[GetUnitId(u)]
endmethod
public static method operator[]= takes unit u, thistype i returns nothing
set thistype.owners[GetUnitId(u)] = i
endmethod
endstruct
//! textmacro RegenBonuses_RegenBonusDefine takes NAME
private struct RegenBonus_$NAME$ extends Bonus
integer minBonus = -2147483648
integer maxBonus = 2147483647
method setBonus takes unit u, integer amount returns integer
local BonusValues values
if not IsUnitIndexed(u) then
debug call ErrorMsg("RegenBonus_$NAME$.setBonus()", "Unit that is not indexed by AutoIndex given")
return 0
endif
set values = BonusValues.getInstance(u)
set values.$NAME$Regen = amount
return amount
endmethod
method getBonus takes unit u returns integer
local BonusValues values
if not IsUnitIndexed(u) then
debug call ErrorMsg("RegenBonus_$NAME$.getBonus()", "Unit that is not indexed by AutoIndex given")
return 0
endif
set values = BonusValues[u]
if values == 0 then
return 0
endif
return values.$NAME$Regen
endmethod
method removeBonus takes unit u returns nothing
call this.setBonus(u, 0)
endmethod
method isValidBonus takes unit u, integer value returns boolean
return IsUnitIndexed(u)
endmethod
endstruct
//! endtextmacro
//! runtextmacro RegenBonuses_RegenBonusDefine("life")
//! runtextmacro RegenBonuses_RegenBonusDefine("mana")
private function OnLeaveMap takes unit u returns nothing
if BonusValues[u] != 0 then
call BonusValues[u].destroy()
endif
endfunction
private function OnInit takes nothing returns nothing
call OnUnitDeindexed(OnLeaveMap)
set BONUS_MANA_REGEN = RegenBonus_mana.create()
set BONUS_LIFE_REGEN_PERCENT = RegenBonus_life.create()
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library MovementBonus initializer OnInit requires BonusMod, AutoIndex
////////////////////////////////////////////////////////////////////////////////
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//@ MovementBonus for BonusMod
//@=============================================================================
//@ Credits:
//@-----------------------------------------------------------------------------
//@ Written by:
//@ Earth-Fury
//@=============================================================================
//@ Requirements:
//@-----------------------------------------------------------------------------
//@ This library requires the BonusMod library, and the AutoIndex library.
//@
//@=============================================================================
//@ Readme:
//@-----------------------------------------------------------------------------
//@ This library provides one new bonus type:
//@
//@ - BONUS_MOVEMENT_SPEED
//@ Modifies a unit's movement speed.
//@
//@ A unit's movement speed can never go below or above the values configured
//@ in the gameplay constants for a map.
//@
//@ You should not modify a unit's movement speed any way other than via
//@ BonusMod. Doing so will cause issues.
//@
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
////////////////////////////////////////////////////////////////////////////////
//==============================================================================
// Configuration:
//==============================================================================
//------------------------------------------------------------------------------
// The following constants must be equal to the equivalent gameplay constants.
// By default, these are set to the default vlues for the gameplay constants.
//------------------------------------------------------------------------------
globals
constant integer MAX_UNIT_MOVEMENT = 400
constant integer MIN_UNIT_MOVEMENT = 150
constant integer MAX_BUILDING_MOVEMENT = 400
constant integer MIN_BUILDING_MOVEMENT = 25
endglobals
//==============================================================================
// End of configuration
//==============================================================================
globals
Bonus BONUS_MOVEMENT_SPEED
endglobals
private function ErrorMsg takes string func, string s returns nothing
call BJDebugMsg("|cffFF0000MovementBonus Error|r|cffFFFF00:|r |cff8080FF" + func + "|r|cffFFFF00:|r " + s)
endfunction
private struct BonusValue
public integer speed = 0
public unit owner
private static thistype array owners
public static thistype array all
public static integer count = 0
public integer index
private static method create takes unit u returns thistype
local thistype this = thistype.allocate()
set this.owner = u
set thistype.owners[GetUnitId(u)] = this
set thistype.all[thistype.count] = this
set this.index = thistype.count
set thistype.count = thistype.count + 1
return this
endmethod
private method onDestroy takes nothing returns nothing
set thistype.owners[GetUnitId(this.owner)] = thistype(0)
set thistype.all[this.index] = thistype.all[thistype.count]
set thistype.count = thistype.count - 1
set this.owner = null
endmethod
public static method getInstance takes unit u returns thistype
if thistype.owners[GetUnitId(u)] == 0 then
return thistype.create(u)
else
return thistype.owners[GetUnitId(u)]
endif
endmethod
public static method operator[] takes unit u returns thistype
return thistype.owners[GetUnitId(u)]
endmethod
public static method operator[]= takes unit u, thistype i returns nothing
set thistype.owners[GetUnitId(u)] = i
endmethod
endstruct
private struct MovementBonus extends Bonus
integer minBonus
integer maxBonus
static method create takes integer min, integer max returns thistype
local thistype this = allocate()
set this.minBonus = min
set this.maxBonus = max
return this
endmethod
method setBonus takes unit u, integer amount returns integer
local BonusValue value
local integer min
local integer max
local integer actualSpeed
if not IsUnitIndexed(u) then
debug call ErrorMsg("MovementBonus.setBonus()", "Unit that is not indexed by AutoIndex given")
return 0
endif
set value = BonusValue.getInstance(u)
if IsUnitType(u, UNIT_TYPE_STRUCTURE) then
set min = MIN_BUILDING_MOVEMENT
set max = MAX_BUILDING_MOVEMENT
else
set min = MIN_UNIT_MOVEMENT
set max = MAX_UNIT_MOVEMENT
endif
set actualSpeed = R2I(GetUnitMoveSpeed(u)) - value.speed
if actualSpeed + amount < min then
debug call ErrorMsg("MovementBonus.setBonus()", "Attempting to set a unit's speed below it's minimum")
set amount = -actualSpeed + min
elseif actualSpeed + amount > max then
debug call ErrorMsg("MovementBonus.setBonus()", "Ateempting to set a unit's speed above it's maximum")
set amount = max - actualSpeed
endif
call SetUnitMoveSpeed(u, actualSpeed + amount)
set value.speed = amount
return amount
endmethod
method getBonus takes unit u returns integer
local BonusValue value
if not IsUnitIndexed(u) then
debug call ErrorMsg("MovementBonus.getBonus()", "Unit that is not indexed by AutoIndex given")
return 0
endif
set value = BonusValue[u]
if value == 0 then
return 0
endif
return value.speed
endmethod
method removeBonus takes unit u returns nothing
call this.setBonus(u, 0)
endmethod
method isValidBonus takes unit u, integer value returns boolean
local integer min
local integer max
local integer currentBonus
local integer actualSpeed
if not IsUnitIndexed(u) then
return false
endif
if IsUnitType(u, UNIT_TYPE_STRUCTURE) then
set min = MIN_BUILDING_MOVEMENT
set max = MAX_BUILDING_MOVEMENT
else
set min = MIN_UNIT_MOVEMENT
set max = MAX_UNIT_MOVEMENT
endif
set actualSpeed = R2I(GetUnitMoveSpeed(u)) - this.getBonus(u)
if actualSpeed + value < min then
return false
elseif actualSpeed + value > max then
return false
else
return true
endif
endmethod
endstruct
private function OnLeaveMap takes unit u returns nothing
if BonusValue[u] != 0 then
call BonusValue[u].destroy()
endif
endfunction
private function OnInit takes nothing returns nothing
local integer min
local integer max
call OnUnitDeindexed(OnLeaveMap)
if MAX_UNIT_MOVEMENT > MAX_BUILDING_MOVEMENT then
set max = MAX_UNIT_MOVEMENT
else
set max = MAX_BUILDING_MOVEMENT
endif
if MIN_UNIT_MOVEMENT < MIN_BUILDING_MOVEMENT then
set min = MIN_UNIT_MOVEMENT
else
set min = MIN_BUILDING_MOVEMENT
endif
set BONUS_MOVEMENT_SPEED = MovementBonus.create(min, max)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library UnitMaxStateBonuses initializer OnInit requires BonusMod, UnitMaxState, AutoIndex
////////////////////////////////////////////////////////////////////////////////
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//@ UnitMaxStateBonuses for BonusMod
//@=============================================================================
//@ Credits:
//@-----------------------------------------------------------------------------
//@ Written by:
//@ Earth-Fury
//@=============================================================================
//@ Requirements:
//@-----------------------------------------------------------------------------
//@ This library requires the BonusMod library, the UnitMaxState library by
//@ Earth-Fury, and the AutoIndex library by grim001.
//@
//@=============================================================================
//@ Readme:
//@-----------------------------------------------------------------------------
//@ This library provides two new bonus types:
//@
//@ - BONUS_LIFE
//@ - BONUS_MANA
//@
//@ These bonuses, of course, raise a unit's maximum life and mana.
//@
//@ Note that if you simply wish to permanently increase or decrease the maximum
//@ life or mana of a unit, you are likely best off using UnitMaxState directly.
//@
//@ The minimum life and mana bonuses are 1 more than -(unit's max state).
//@ That is to say, these bonuses can not fully remove a unit's life or mana.
//@
//@ There is no maximum bonus.
//@
//@ There is nothing to configure.
//@
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
////////////////////////////////////////////////////////////////////////////////
globals
Bonus BONUS_LIFE
Bonus BONUS_MANA
endglobals
private function ErrorMsg takes string func, string s returns nothing
call BJDebugMsg("|cffFF0000UnitMaxStateBonus Error|r|cffFFFF00:|r |cff8080FF" + func + "|r|cffFFFF00:|r " + s)
endfunction
private struct BonusValues
public integer mana = 0
public integer life = 0
public unit owner
private static thistype array owners
private static method create takes unit u returns thistype
local thistype this = thistype.allocate()
set this.owner = u
set thistype.owners[GetUnitId(u)] = this
return this
endmethod
private method onDestroy takes nothing returns nothing
set thistype.owners[GetUnitId(this.owner)] = thistype(0)
set this.owner = null
endmethod
public static method getInstance takes unit u returns thistype
if thistype.owners[GetUnitId(u)] == 0 then
return thistype.create(u)
else
return thistype.owners[GetUnitId(u)]
endif
endmethod
public static method operator[] takes unit u returns thistype
return thistype.owners[GetUnitId(u)]
endmethod
public static method operator[]= takes unit u, thistype i returns nothing
set thistype.owners[GetUnitId(u)] = i
endmethod
endstruct
//! textmacro UnitMaxStateBonus_DefineBonus takes NAME, STATE, MIN
private struct MaxStateBonus_$NAME$ extends Bonus
integer minBonus = -2147483648
integer maxBonus = 2147483647
method setBonus takes unit u, integer amount returns integer
local BonusValues values
local integer actual
local integer new
local real factor
if not IsUnitIndexed(u) then
debug call ErrorMsg("MaxStateBonus_$NAME$.setBonus()", "Unit that is not indexed by AutoIndex given")
return 0
endif
set values = BonusValues.getInstance(u)
set actual = R2I(GetUnitState(u, UNIT_STATE_MAX_$STATE$)) - values.$NAME$
set new = actual + amount
set factor = GetUnitState(u, UNIT_STATE_$STATE$) / GetUnitState(u, UNIT_STATE_MAX_$STATE$)
if new < $MIN$ then
if actual < $MIN$ then
set values.$NAME$ = 0
else
set values.$NAME$ = -actual + $MIN$
endif
call SetUnitState(u, UNIT_STATE_$STATE$, $MIN$)
call SetUnitMaxState(u, UNIT_STATE_MAX_$STATE$, $MIN$)
return values.$NAME$
else
set values.$NAME$ = amount
call SetUnitMaxState(u, UNIT_STATE_MAX_$STATE$, new)
call SetUnitState(u, UNIT_STATE_$STATE$, new * factor)
return values.$NAME$
endif
endmethod
method getBonus takes unit u returns integer
local BonusValues values
local integer actual
local integer new
local real factor
if not IsUnitIndexed(u) then
debug call ErrorMsg("MaxStateBonus_$NAME$.getBonus()", "Unit that is not indexed by AutoIndex given")
return 0
endif
set values = BonusValues[u]
if values == 0 then
return 0
endif
set values.$NAME$ = R2I(GetUnitState(u, UNIT_STATE_MAX_$STATE$)) - (R2I(GetUnitState(u, UNIT_STATE_MAX_$STATE$)) - values.$NAME$)
return values.$NAME$
endmethod
method removeBonus takes unit u returns nothing
call this.setBonus(u, 0)
endmethod
method isValidBonus takes unit u, integer value returns boolean
local integer currentBonus
if not IsUnitIndexed(u) then
return false
endif
set currentBonus = this.getBonus(u)
return R2I(GetUnitState(u, UNIT_STATE_MAX_$STATE$)) - currentBonus + value >= $MIN$
endmethod
endstruct
//! endtextmacro
//! runtextmacro UnitMaxStateBonus_DefineBonus("life", "LIFE", "1")
//! runtextmacro UnitMaxStateBonus_DefineBonus("mana", "MANA", "1")
private function OnLeaveMap takes unit u returns nothing
if BonusValues[u] != 0 then
call BonusValues[u].destroy()
endif
endfunction
private function OnInit takes nothing returns nothing
call OnUnitDeindexed(OnLeaveMap)
set BONUS_LIFE = MaxStateBonus_life.create()
set BONUS_MANA = MaxStateBonus_mana.create()
endfunction
endlibrary
//TESH.scrollpos=95
//TESH.alwaysfold=0
//* Contains code to guide you during testing the map.
scope DemoMap initializer Init
globals
private string blue = "|cff99b4d1"
private string gold = "|cffdaa520"
private sound hint
private integer count = 1
private boolean open = false
private boolean enabled = true
private UIWindow window
endglobals
private function Print takes string s returns nothing
call DisplayTimedTextToPlayer(GetLocalClient(), 0, 0, 15, s)
endfunction
private function Highlight takes nothing returns nothing
local string flash = "DRAB"
local lightning one = AddLightning(flash, false, window.originX, window.originY, window.originX, window.originY + window.height)
local lightning two = AddLightning(flash, false, window.originX, window.originY + window.height, window.originX + window.width, window.originY + window.height)
local lightning three = AddLightning(flash, false, window.originX + window.width, window.originY, window.originX, window.originY)
local lightning four = AddLightning(flash, false, window.originX + window.width, window.originY + window.height, window.originX + window.width, window.originY)
call TriggerSleepAction(5.)
call DestroyLightning(one)
call DestroyLightning(two)
call DestroyLightning(three)
call DestroyLightning(four)
set one = null
set two = null
set three = null
set four = null
endfunction
private function NewHint takes string s returns nothing
call ClearTextMessages()
call StartSound(hint)
call Print(gold + "Hint [" + I2S(count) + "] - " + s)
call Print(" ")
set count = count + 1
endfunction
private function Hints2 takes nothing returns nothing
call Print(blue + "User controll enabled.")
//*
call TriggerSleepAction(10.)
call NewHint("Access an Inventory")
call Print(blue + "You can open & close a units inventory by clicking the 'Inventory' ability.")
call Print(blue + "Or simply press the hotkey |r" + gold + "[I]")
call Print(" ")
call Print(blue + "In your own map, you can decide, how to trigger open and close")
call Print(blue + "For example via Esc key or in a spellbook ....")
//*
call TriggerSleepAction(10.)
call NewHint("Custom Items")
call Print(blue + "Nearly every item that drops in this map is registered as custom item.")
call Print(blue + "On item pickup event, they are directly move to the units backpack.")
call Print(" ")
call Print(blue + "Of course only when there is room ...")
endfunction
private function StartHints takes nothing returns nothing
local UIScreen screen = 1
call Print(gold + "Welcome to the demo map of Inventory!")
//*
call TriggerSleepAction(1.)
if not enabled then
return
endif
call NewHint("Introduction")
call Print(blue + "This tutorial will familiarize you with Inventory.")
call Print(blue + "For further questions, please make a post")
call Print(blue + "in the official forum thread on TheHiveWorkshop.com")
call Print(" ")
call Print(blue + "During the tutorial the user controll is disabled.")
call Print(blue + "Press|r " + gold + "Esc|r" + blue + " to cancel the tutorial.")
//*
call TriggerSleepAction(5.)
if not enabled then
return
endif
call screen.show(true)
call NewHint("Inventory Interface")
call Print(blue + "What you see is the entire inventory.")
set window = screen.getWindowOfType(InventoryControll.typeid)
call ExecuteFunc(Highlight.name)
//*
call TriggerSleepAction(5.)
if not enabled then
return
endif
call NewHint("The Backpack")
call Print(blue + "This one is the backpack.")
call Print(blue + "Items picked up are directly moved there.")
set window = screen.getWindowOfType(InventoryBackpack.typeid)
call ExecuteFunc(Highlight.name)
//*
call TriggerSleepAction(5.)
if not enabled then
return
endif
call NewHint("The Equipment")
call Print(blue + "This one is the equipment.")
call Print(blue + "Items moved there will be equipped to the unit.")
call Print(blue + "Inventory fires an equip and unequip event.")
set window = screen.getWindowOfType(InventoryEquipment.typeid)
call ExecuteFunc(Highlight.name)
//*
call TriggerSleepAction(5.)
if not enabled then
return
endif
call NewHint("The Native Inventory")
call Print(blue + "This one is a sync of your native inventory.")
set window = screen.getWindowOfType(InventoryNativeInventory.typeid)
call ExecuteFunc(Highlight.name)
//*
call TriggerSleepAction(4.)
if not enabled then
return
endif
call NewHint("The Dummy Window")
call Print(blue + "This one is the dummy window. Using a dummy is optional.")
call Print(blue + "Clicking the dummy window shows or hides your hero details.")
set window = screen.getWindowOfType(InventoryDummy.typeid)
call ExecuteFunc(Highlight.name)
//*
call TriggerSleepAction(5.)
if not enabled then
return
endif
call screen.show(false)
call EnableUserControl(true)
call ShowInterface(true, .5)
call ExecuteFunc(Hints2.name)
endfunction
private function OnEsc takes nothing returns nothing
if enabled then
set enabled = false
call EnableUserControl(true)
call ShowInterface(true, .5)
call ClearTextMessages()
call Print(" ")
call Print(gold + "Hints permanent disabled!")
endif
endfunction
private function OnDeath takes nothing returns nothing
local unit u = GetTriggerUnit()
local real x = GetUnitX(u)
local real y = GetUnitY(u)
if not IsUnitType(u, UNIT_TYPE_HERO) then
call CreateItem(ChooseRandomItemEx(ITEM_TYPE_PERMANENT, 8), x, y)
call CreateItem(ChooseRandomItemEx(ITEM_TYPE_PURCHASABLE, 8), x, y)
endif
set u = null
endfunction
private function Init takes nothing returns nothing
local trigger trig = CreateTrigger()
set hint = CreateSound("Sound\\Interface\\Hint.wav", false, false, false, 10, 10, "CombatSoundsEAX")
call TriggerRegisterPlayerEventEndCinematic(trig, Player(0))
call TriggerAddAction(trig, function OnEsc)
call EnableUserControl(false)
call ShowInterface(false, 1.)
call ExecuteFunc(StartHints.name)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function OnDeath)
endfunction
endscope