//TESH.scrollpos=0
//TESH.alwaysfold=0
Name | Type | is_array | initial_value |
HeroXPConstant | integer | No | |
HeroXPLevelFactor | integer | No | |
HeroXPPrevLevelFactor | integer | No | |
HeroXPRequired | integer | No | |
LocalFiles_WarningMessage | string | No | |
MapName | string | No | |
SaveAbilityType | abilcode | Yes | |
SaveAbilityTypeMax | integer | No | |
SaveCount | integer | No | |
SaveHeroName | boolean | No | |
SaveItemType | itemcode | Yes | |
SaveItemTypeMax | integer | No | |
SaveLoad_HeroName | boolean | No | |
SaveLoad_Slot | integer | No | |
SaveLoadEvent | real | No | |
SaveLoadEvent_Code | string | No | |
SaveLoadEvent_Player | player | No | |
SaveMaxValue | integer | Yes | |
SavePlayerHero | unit | Yes | |
SaveShowCode | boolean | No | |
SaveTempInt | integer | No | |
SaveTempReal | real | No | |
SaveTempString | string | No | |
SaveTempUnit | unit | No | |
SaveUnitMaxStat | integer | No | |
SaveUnitType | unitcode | Yes | |
SaveUnitTypeMax | integer | No | |
SaveValue | integer | Yes |
library SaveHelper requires Savecode, Sync, FileIO
// helper for GUI
globals
integer array CurrentCharSaveSlot
private real array HeroProperName_Data
private unit array HeroProperName
private string array TempHeroName
endglobals
function GetStringLine takes string source, integer whichLine returns string
local integer length = StringLength(source)
local integer i = 0
local string c = ""
local integer currentline = 0
local string currentlinestr=""
loop
exitwhen i > length
set c = SubString(source, i, i + 2)
if (c == "|n") then
set currentline=currentline+1
if (currentline==whichLine) then
return currentlinestr
else
set currentlinestr = ""
endif
else
set currentlinestr = currentlinestr + SubString(source, i, i + 1)
endif
set i = i + 1
endloop
return SubString(currentlinestr, 1, 999)
endfunction
function SaveCode_AddValue takes integer value, integer maxvalue returns nothing
set udg_SaveCount = udg_SaveCount + 1
set udg_SaveValue[udg_SaveCount] = value
set udg_SaveMaxValue[udg_SaveCount] = maxvalue
endfunction
function SaveCode_LoadValue takes integer savecode, integer index returns Savecode
return Savecode(savecode).Decode(udg_SaveMaxValue[index])
endfunction
function SaveCode_LoadNextValue takes nothing returns nothing
set udg_SaveValue[udg_SaveCount] = SaveCode_LoadValue(udg_SaveTempInt, udg_SaveCount)
endfunction
function SaveCode_U2I takes integer id returns integer
local integer i = 0
loop
exitwhen i > udg_SaveUnitTypeMax
if (id == udg_SaveUnitType[i]) then
return i
endif
set i = i + 1
endloop
return 0
endfunction
function SaveCode_I2I takes integer id returns integer
local integer i = 0
loop
exitwhen i > udg_SaveItemTypeMax
if (id == udg_SaveItemType[i]) then
return i
endif
set i = i + 1
endloop
return 0
endfunction
function FindLevelXP takes integer level returns real
local real xp = udg_HeroXPLevelFactor // level 1
local integer i = 1
loop
exitwhen i > level
set xp = (xp*udg_HeroXPPrevLevelFactor) + (i+1) * udg_HeroXPLevelFactor
set i = i + 1
endloop
return xp-udg_HeroXPLevelFactor
endfunction
function DeleteCharSlot takes player p, integer slot returns nothing
local File file
local string s
if (GetLocalPlayer() == p) then
call GameData.delete("Char"+I2S(slot))
endif
endfunction
function SaveCode_Save takes player p, integer slot returns nothing
local string s
local integer i = 0
local integer pid = GetPlayerId(p)
local Savecode savecode
local Savecode loadcode
local string fname = "Char"+I2S(slot)
local File file
if (udg_SaveCount == -1) then
return
endif
if (GetLocalPlayer() == p) then
if (slot == 10) then // autosave
set fname = "Autosave"
else
set s = GameData.read(fname)
if (slot < 10 and slot != CurrentCharSaveSlot[pid] and s != null and StringLength(s) > 0) then
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 30, "That save slot is in use.")
return
endif
if (slot != CurrentCharSaveSlot[pid]) then
//call DeleteCharSlot(p, CurrentCharSaveSlot[pid])
endif
endif
endif
if (slot < 10) then
set CurrentCharSaveSlot[pid] = slot
endif
set savecode = Savecode.create()
loop
exitwhen i > udg_SaveCount
call savecode.Encode(udg_SaveValue[i], udg_SaveMaxValue[i])
set i = i + 1
endloop
set s = savecode.Save(p, 1)
if (GetLocalPlayer() == p) then
if (udg_SaveHeroName) then
call GameData.write(fname, GetObjectName(udg_SaveUnitType[udg_SaveValue[udg_SaveCount]]) + " (" + GetHeroProperName(udg_SavePlayerHero[pid]) + ")|n" + s)
else
call GameData.write(fname, GetObjectName(udg_SaveUnitType[udg_SaveValue[udg_SaveCount]]) + "|n" + s)
endif
endif
if (slot != 10 and udg_SaveShowCode) then
call DisplayTimedTextToPlayer(p, 0, 0, 0, Savecode_colorize(s))
endif
set udg_SaveCount = -1
set udg_SaveValue[0] = -1
set udg_SaveMaxValue[0] = -1
set udg_SaveLoad_Slot = -1
endfunction
private function OnRecieveString takes nothing returns boolean
local SyncData d = GetSyncedData()
set udg_SaveLoadEvent_Code = d.readString(0)
set udg_SaveLoadEvent_Player = d.from
set udg_SaveLoadEvent = 1.
set udg_SaveLoadEvent = -1
call d.destroy()
return false
endfunction
function t_SaveCode_LoadHeroWithProperName takes nothing returns nothing
local integer pid = R2I(HeroProperName_Data[0])
local player p = Player(pid)
local string s = TempHeroName[pid]
local integer i = 0
local integer a = 0
local integer l = StringLength(s)
local unit u = null
local integer id = CurrentCharSaveSlot[JASS_MAX_ARRAY_SIZE-1]
local real x = HeroProperName_Data[2]
local real y = HeroProperName_Data[3]
local real f = HeroProperName_Data[4]
loop
exitwhen i > l or SubString(s, i, i + 1) == "("
set i = i + 1
endloop
if (i >= l) then
set HeroProperName[pid] = CreateUnit(p, id, x, y, f)
return
endif
set a = i
loop
exitwhen SubString(s, a, a + 1) == ")"
set a = a + 1
endloop
set s = SubString(s, i + 1, a)
set i = 0
loop
exitwhen i > 50
if (u != null) then
call RemoveUnit(u)
set u = null
endif
set u = CreateUnit(p, id, x, y, f)
if (not IsUnitType(u, UNIT_TYPE_HERO)) then
set HeroProperName[pid] = u
set u = null
return
endif
if (GetHeroProperName(u) == s) then
set HeroProperName[pid] = u
set u = null
return
endif
set i = i + 1
endloop
set HeroProperName[pid] = u
set u = null
endfunction
function SaveCode_LoadHeroWithProperName takes player p, integer id, real x, real y, real f returns unit
local integer pid = GetPlayerId(p)
set CurrentCharSaveSlot[JASS_MAX_ARRAY_SIZE-1] = id
set HeroProperName_Data[0] = I2R(pid)
//set HeroProperName_Data[1] = id
set HeroProperName_Data[2] = x
set HeroProperName_Data[3] = y
set HeroProperName_Data[4] = f
call ExecuteFunc("t_SaveCode_LoadHeroWithProperName")
return HeroProperName[pid]
endfunction
function SaveCode_ReadSlot takes player p, integer slot returns string
local File file
local string val = ""
local string header = ""
local string line = ""
local integer i = 0
local string fname = "Char" + I2S(slot)
// load latest save file from disk (only for the triggering player)
if (GetLocalPlayer() == p) then
if (slot == 10) then
set fname = "Autosave"
endif
set val = GameData.read(fname)
set header = GetStringLine(val, 1)
if (header == null or StringLength(header) <= 0) then
if (slot != 10) then
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Slot #" + I2S(slot+1) + " is emtpy.")
else
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Your autosave slot is empty.")
endif
else
set TempHeroName[GetPlayerId(p)]=header
endif
endif
return val
endfunction
function SaveCode_Load takes player p, integer slot returns nothing
local integer id = GetPlayerId(p)
local string val = SaveCode_ReadSlot(p, slot)
local SyncData data = SyncData.create(Player(id))
call data.addString(val, 64)
call data.addEventListener(Filter(function OnRecieveString))
call data.start()
//return val
endfunction
function SaveCharToSlot takes unit u, integer slot returns nothing
local integer pid = GetPlayerId(GetOwningPlayer(u))
local unit temp = udg_SavePlayerHero[pid]
set udg_SaveLoad_Slot = slot
set udg_SavePlayerHero[pid] = u
if (TriggerEvaluate(gg_trg_Save_GUI)) then
call TriggerExecute(gg_trg_Save_GUI)
endif
set udg_SavePlayerHero[pid] = temp
endfunction
function GetSaveSlotTitle takes player p, integer slot returns string
local File file
local string header = null
local string fname
if (GetLocalPlayer() == p) then
if (slot == 10) then
set fname = "Autosave"
else
set fname = "Char" + I2S(slot)
endif
set header = GetStringLine(GameData.read(fname), 1)
endif
return header
endfunction
function LoadSaveSlot_OnRecieveString takes nothing returns boolean
local SyncData data = GetSyncedData()
local integer id = GetPlayerId(data.from)
local Savecode loadcode
if (udg_SaveHeroName) then
set TempHeroName[id] = data.readString(0)
set udg_SaveLoadEvent_Code = data.readString(1)
else
set udg_SaveLoadEvent_Code = data.readString(0)
endif
set udg_SaveLoadEvent_Player = data.from
set udg_SaveTempReal = data.timeElapsed
set udg_SaveLoadEvent = 1.
set udg_SaveLoadEvent = -1
call data.destroy()
return false
endfunction
function LoadSaveSlot takes player p, integer slot returns boolean
local integer id
local string val = ""
local string header = ""
local integer i = 0
local File file
local string line
local SyncData data
local string fname
if (i > 10) then
return false
endif
// load latest save file from disk (only for the triggering player)
if (GetLocalPlayer() == p) then
if (slot == 10) then
set fname = "Autosave"
else
set fname = "Char" + I2S(slot)
endif
set val = GameData.read(fname)
set header = GetStringLine(val, 1)
set val = GetStringLine(GameData.read(fname), 2)
if (header == null or StringLength(header) <= 0) then
if (slot != 10) then
call DisplayTextToPlayer(p, 0, 0, "Slot #" + I2S(slot+1) + " is emtpy.")
else
call DisplayTextToPlayer(p, 0, 0, "Your autosave slot is empty.")
endif
else
set TempHeroName[GetPlayerId(p)]=header
endif
endif
set id = GetPlayerId(p)
if (GetLocalPlayer() == p and val != "") then
call DisplayTextToPlayer(p, 0, 0, "Loading your " + header)
endif
set data = SyncData.create(Player(id))
if (udg_SaveHeroName) then
call data.addString(header, 32)
endif
call data.addString(val, 64)
call data.addEventListener(Filter(function LoadSaveSlot_OnRecieveString))
call data.start()
set CurrentCharSaveSlot[id] = slot
return false
endfunction
endlibrary
library SampleSavevJass initializer Init requires SaveHelper, FileIO
// uses GUI variables
private function Actions takes nothing returns boolean
local integer i = 0
local integer l = 0
local integer c = 0
local Savecode savecode = Savecode.create()
local string s
local player p = GetTriggerPlayer()
local integer pid = GetPlayerId(p)
local unit u = udg_SavePlayerHero[pid]
local integer uidSmall = SaveCode_U2I(GetUnitTypeId(u))
local string fname = "CharVJASS"
// save abilities
loop
exitwhen i > udg_SaveAbilityTypeMax
set l = GetUnitAbilityLevel(u, udg_SaveAbilityType[i])
if (l > 0) then
call savecode.Encode(l, 10)
call savecode.Encode(i, udg_SaveAbilityTypeMax)
set c = c + 1
endif
set i = i + 1
endloop
// save amount of skills and skill points
call savecode.Encode(c, udg_SaveAbilityTypeMax)
call savecode.Encode(GetHeroSkillPoints(u), 999)
// save items
set i = 0
loop
exitwhen i > 5
set l = GetItemTypeId(UnitItemInSlot(u, i))
call savecode.Encode(SaveCode_I2I(l), udg_SaveItemTypeMax)
set i = i + 1
endloop
// save stats
call savecode.Encode(GetHeroStr(u, false), 999)
call savecode.Encode(GetHeroAgi(u, false), 999)
call savecode.Encode(GetHeroInt(u, false), 999)
// save xp
call savecode.Encode(R2I( (GetHeroXP(u)) / FindLevelXP(GetHeroLevel(u)) * 100), 100) // percentage
call savecode.Encode(GetHeroLevel(u), 100)
call savecode.Encode(uidSmall, udg_SaveUnitTypeMax)
// generate code and save to disk
set s = savecode.Save(p, 1)
if (GetLocalPlayer() == p) then
if (udg_SaveHeroName) then
call GameData.write(fname, GetObjectName(udg_SaveUnitType[udg_SaveValue[udg_SaveCount]]) + " (" + GetHeroProperName(udg_SavePlayerHero[pid]) + ")|n" + s)
else
call GameData.write(fname, GetObjectName(udg_SaveUnitType[udg_SaveValue[udg_SaveCount]]) + "|n" + s)
endif
endif
call DisplayTimedTextToPlayer(p, 0, 0, 0, Savecode_colorize(s))
set u = null
return false
endfunction
private function Init takes nothing returns nothing
local integer i = 0
local trigger t = CreateTrigger()
loop
exitwhen i > 11
call TriggerRegisterPlayerChatEvent(t, Player(i), "-save", true )
set i = i + 1
endloop
call TriggerAddCondition(t, Filter(function Actions))
endfunction
endlibrary
library SampleLoadvJass initializer Init requires Sync, SaveHelper, PlayerUtils
function LoadHeroFromString takes player p, string s returns nothing
local unit u
local integer array values
local integer i = GetPlayerId(p)
local Savecode loadcode
set loadcode = Savecode.create()
// check the code
if (not loadcode.Load(p, s, 1)) then
if (s != "") then
call DisplayTextToPlayer(p, 0 , 0, "invalid code: " + s)
endif
return
endif
// display success
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Loaded " + User(i).nameColored + "'s character")
// remove old hero
call RemoveUnit(udg_SavePlayerHero[i])
set udg_SavePlayerHero[i] = null
// create new one
set u = CreateUnit(p, udg_SaveUnitType[loadcode.Decode(udg_SaveUnitTypeMax)], 0, 0, 0)
set udg_SavePlayerHero[i] = u
// load XP %
set values[0] = loadcode.Decode(100)
set values[1] = loadcode.Decode(100)
set values[2] = R2I((values[1] / 100.) * FindLevelXP(values[0]))
call SetHeroXP(u, values[2], false)
// load hero level
if (GetHeroLevel(u) != values[0]) then
call SetHeroLevel(u, values[0], false)
endif
// attributes
call SetHeroInt(u, loadcode.Decode(999), false)
call SetHeroAgi(u, loadcode.Decode(999), false)
call SetHeroStr(u, loadcode.Decode(999), false)
// items
set i=0
loop
exitwhen i > 5
call UnitAddItemToSlotById(u, udg_SaveItemType[loadcode.Decode(udg_SaveItemTypeMax)], 5-i)
set i = i + 1
endloop
// skill points
set values[0] = loadcode.Decode(999)
call UnitModifySkillPoints(u, 0)
call UnitModifySkillPoints(u, values[0] - GetHeroSkillPoints(u))
// abilities
set values[1] = loadcode.Decode(udg_SaveAbilityTypeMax)
set i = 0
loop
exitwhen i == values[1]
set values[0] = udg_SaveAbilityType[loadcode.Decode(udg_SaveAbilityTypeMax)]
call UnitAddAbility(u, values[0])
call SetUnitAbilityLevel(u, values[0], loadcode.Decode(10))
set i = i + 1
endloop
if (GetLocalPlayer() == p) then
call ClearSelection()
call SelectUnit(u, true)
endif
call DisplayTextToPlayer(p, 0, 0, "|cff009f00Character loaded!|r")
endfunction
private function OnReceiveCode takes nothing returns boolean
local SyncData d = GetSyncedData()
call LoadHeroFromString(d.from, d.readString(0))
call d.destroy()
return false
endfunction
private function Actions takes nothing returns boolean
local player p = GetTriggerPlayer()
local string s = SubString(GetEventPlayerChatString(), 0, 6)
local SyncData data
if (s == "-load ") then
set s = SubString(GetEventPlayerChatString(), 6, 999)
if (S2I(s) > 0) then
set s = SaveCode_ReadSlot(p, S2I(s)-1)
set data = SyncData.create(p)
call data.addString(s, 64)
call data.addEventListener(Filter(function OnReceiveCode))
call data.start()
else
set s = SubString(GetEventPlayerChatString(), 6, 999)
call LoadHeroFromString(p, s)
endif
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
loop
exitwhen i > 11
call TriggerRegisterPlayerChatEvent(t, Player(i), "-load", false )
set i = i + 1
endloop
call TriggerAddCondition(t, Filter(function Actions))
endfunction
endlibrary
library SampleDialogSystem initializer Init requires SaveHelper
globals
private dialog array Dialog
private button array Button
private button array PlayerButton[25][10]
private trigger T=CreateTrigger()
endglobals
private function GetTitle takes player p, integer slot returns string
local string s = GetSaveSlotTitle(p, slot)
if (s == null or s == "") then
return "Empty"
endif
return s
endfunction
private function OnButtonClick takes nothing returns boolean
local button b = GetClickedButton()
local player p = GetTriggerPlayer()
local string s = ""
local integer pid = GetPlayerId(p)
local boolean safe = MemoryHacksEnabled() or (File.enabled and GetLocalPlayer() == p)
local User u = User(pid)
if (b == Button[(u.id*12)+0]) then // save char
call DialogClear(Dialog[(u.id*12)+2])
call DialogSetMessage(Dialog[(u.id*12)+2], "|cffff8000Save Character|r")
if (not safe) then
call ClearTextMessages()
call DisplayTimedTextToPlayer(p, 0, 0, 60000, udg_LocalFiles_WarningMessage)
endif
set PlayerButton[pid][10] = DialogAddButton(Dialog[(u.id*12)+2], "|cffffcc00Slot #1 - |cffffffff" + GetTitle(p, 0) + "|r", 0)
set PlayerButton[pid][11] = DialogAddButton(Dialog[(u.id*12)+2], "|cffffcc00Slot #2 - |cffffffff" + GetTitle(p, 1), 0)
set PlayerButton[pid][12] = DialogAddButton(Dialog[(u.id*12)+2], "|cffffcc00Slot #3 - |cffffffff" + GetTitle(p, 2), 0)
set PlayerButton[pid][13] = DialogAddButton(Dialog[(u.id*12)+2], "|cffffcc00Slot #4 - |cffffffff" + GetTitle(p, 3), 0)
set PlayerButton[pid][14] = DialogAddButton(Dialog[(u.id*12)+2], "|cffffcc00Slot #5 - |cffffffff" + GetTitle(p, 4), 0)
call DialogAddButton(Dialog[(u.id*12)+2], "|cffff8000Close", 0)
call DialogDisplay(GetLocalPlayer(), Dialog[(u.id*12)+2], safe)
elseif(b == Button[(u.id*12)+1]) then // load char
if (not safe) then
call ClearTextMessages()
call ClearTextMessages()
call DisplayTimedTextToPlayer(p, 0, 0, 60000, udg_LocalFiles_WarningMessage)
endif
call DialogClear(Dialog[(u.id*12)+1])
call DialogSetMessage(Dialog[(u.id*12)+1], "|cffff8000Load Character|r")
set PlayerButton[pid][20] = DialogAddButton(Dialog[(u.id*12)+1], "|cffffcc00Autosave - |cffffffff" + GetTitle(p, 10), 0)
set PlayerButton[pid][0] = DialogAddButton(Dialog[(u.id*12)+1], "|cffffcc00Slot #1 - |cffffffff" + GetTitle(p, 0), 0)
set PlayerButton[pid][1] = DialogAddButton(Dialog[(u.id*12)+1], "|cffffcc00Slot #2 - |cffffffff" + GetTitle(p, 1), 0)
set PlayerButton[pid][2] = DialogAddButton(Dialog[(u.id*12)+1], "|cffffcc00Slot #3 - |cffffffff" + GetTitle(p, 2), 0)
set PlayerButton[pid][3] = DialogAddButton(Dialog[(u.id*12)+1], "|cffffcc00Slot #4 - |cffffffff" + GetTitle(p, 3), 0)
set PlayerButton[pid][4] = DialogAddButton(Dialog[(u.id*12)+1], "|cffffcc00Slot #5 - |cffffffff" + GetTitle(p, 4), 0)
call DialogAddButton(Dialog[(u.id*12)+1], "|cffff8000Close", 0)
call DialogDisplay(p, Dialog[(u.id*12)+1], safe)
elseif(b == Button[(u.id*12)+2]) then // load char
if (not safe) then
call ClearTextMessages()
call DisplayTimedTextToPlayer(p,0,0,60000, udg_LocalFiles_WarningMessage)
endif
call DialogClear(Dialog[(u.id*12)+3])
call DialogSetMessage(Dialog[(u.id*12)+3], "|cffff8000Delete Character|r")
set PlayerButton[pid][5] = DialogAddButton(Dialog[(u.id*12)+3], "|cffff0000Delete - |cffffffff" + GetTitle(p, 0), 0)
set PlayerButton[pid][6] = DialogAddButton(Dialog[(u.id*12)+3], "|cffff0000Delete - |cffffffff" + GetTitle(p, 1), 0)
set PlayerButton[pid][7] = DialogAddButton(Dialog[(u.id*12)+3], "|cffff0000Delete - |cffffffff" + GetTitle(p, 2), 0)
set PlayerButton[pid][8] = DialogAddButton(Dialog[(u.id*12)+3], "|cffff0000Delete - |cffffffff" + GetTitle(p, 3), 0)
set PlayerButton[pid][9] = DialogAddButton(Dialog[(u.id*12)+3], "|cffff0000Delete - |cffffffff" + GetTitle(p, 4), 0)
call DialogAddButton(Dialog[(u.id*12)+3], "|cffff8000Close", 0)
call DialogDisplay(p, Dialog[(u.id*12)+3], safe)
elseif(b == PlayerButton[pid][0]) then
call LoadSaveSlot(p, 0)
elseif(b == PlayerButton[pid][1]) then
call LoadSaveSlot(p, 1)
elseif(b == PlayerButton[pid][2]) then
call LoadSaveSlot(p, 2)
elseif(b == PlayerButton[pid][3]) then
call LoadSaveSlot(p, 3)
elseif(b == PlayerButton[pid][4]) then
call LoadSaveSlot(p, 4)
elseif(b == PlayerButton[pid][20]) then
call LoadSaveSlot(p, 10)
elseif(b == PlayerButton[pid][5]) then
call DeleteCharSlot(p, 0)
elseif(b == PlayerButton[pid][6]) then
call DeleteCharSlot(p, 1)
elseif(b == PlayerButton[pid][7]) then
call DeleteCharSlot(p, 2)
elseif(b == PlayerButton[pid][8]) then
call DeleteCharSlot(p, 3)
elseif(b == PlayerButton[pid][9]) then
call DeleteCharSlot(p, 4)
elseif(b == PlayerButton[pid][10]) then
call SaveCharToSlot(udg_SavePlayerHero[pid], 0)
elseif(b == PlayerButton[pid][11]) then
call SaveCharToSlot(udg_SavePlayerHero[pid], 1)
elseif(b == PlayerButton[pid][12]) then
call SaveCharToSlot(udg_SavePlayerHero[pid], 2)
elseif(b == PlayerButton[pid][13]) then
call SaveCharToSlot(udg_SavePlayerHero[pid], 3)
elseif(b == PlayerButton[pid][14]) then
call SaveCharToSlot(udg_SavePlayerHero[pid], 4)
endif
return false
endfunction
private function ShowMenu takes nothing returns boolean
local player p = GetTriggerPlayer()
local integer i = GetPlayerId(p)
if (GetLocalPlayer() == p) then
call DialogSetMessage(Dialog[(i*12)+0], "|cffff8000Main Menu|r")
call DialogDisplay(p, Dialog[(i*12)+0], true)
endif
return false
endfunction
private function InitDialog takes nothing returns nothing
local integer i = 0
local trigger t = CreateTrigger()
local User u
loop
exitwhen i == User.AmountPlaying
set u = User.fromPlaying(i)
set Dialog[(u.id*12)+0] = DialogCreate()
set Button[(u.id*12)+0] = DialogAddButton(Dialog[(u.id*12)+0], "|cffffcc00Save Character", 0)
set Button[(u.id*12)+1] = DialogAddButton(Dialog[(u.id*12)+0], "|cffffcc00Load Character", 0)
set Button[(u.id*12)+2] = DialogAddButton(Dialog[(u.id*12)+0], "|cffffcc00Delete Character", 0)
call DialogAddButton(Dialog[(u.id*12)+0], "|cffff8000Close", 0)
set Dialog[(u.id*12)+1] = DialogCreate()
set Dialog[(u.id*12)+2] = DialogCreate()
set Dialog[(u.id*12)+3] = DialogCreate()
call TriggerRegisterDialogEvent(t, Dialog[(u.id*12)+0])
call TriggerRegisterDialogEvent(t, Dialog[(u.id*12)+1])
call TriggerRegisterDialogEvent(t, Dialog[(u.id*12)+2])
call TriggerRegisterDialogEvent(t, Dialog[(u.id*12)+3])
set i = i + 1
endloop
call TriggerAddCondition(t, Filter(function OnButtonClick))
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
loop
set CurrentCharSaveSlot[i] = -1
call TriggerRegisterPlayerEvent(t, Player(i), EVENT_PLAYER_END_CINEMATIC)
set i = i + 1
exitwhen i == bj_MAX_PLAYER_SLOTS
endloop
call TriggerAddCondition(t, Filter(function ShowMenu))
call TimerStart(CreateTimer(), 0, false, function InitDialog)
endfunction
endlibrary
library Sync requires SyncInteger, optional PlayerUtils
/***************************************************************
*
* v1.2.5, by TriggerHappy
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*
* This library allows you to quickly synchronize async data such as,
* camera position, or a the contents of a local file, to all players
* in the map by using the game cache.
*
* Full Documentation: -http://www.hiveworkshop.com/forums/pastebin.php?id=p4f84s
*
* _________________________________________________________________________
* 1. Installation
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Copy the script to your map and save it (requires JassHelper *or* JNGP)
*
* SyncInteger: https://www.hiveworkshop.com/threads/syncinteger.278674/
* PlayerUtils: https://www.hiveworkshop.com/threads/playerutils.278559/
* _________________________________________________________________________
* 2. API
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* struct SyncData
*
* method start takes nothing returns nothing
* method startChunk takes integer i, integer end returns nothing
* method refresh takes nothing returns nothing
* method destroy takes nothing returns nothing
*
* method addInt takes integer i returns nothing
* method addReal takes integer i returns nothing
* method addString takes string s, integer len returns nothing
* method addBool takes booleanflag returns nothing
*
* method readInt takes integer index returns integer
* method readReal takes integer index returns integer
* method readString takes integer index returns string
* method readBool takes integer index returns boolean
*
* method hasInt takes integer index returns boolean
* method hasReal takes integer index returns boolean
* method hasString takes integer index returns boolean
* method hasBool takes integer index returns boolean
*
* method isPlayerDone takes player p returns boolean
* method isPlayerIdDone takes integer pid returns boolean
*
* method addEventListener takes filterfunc func returns nothing
*
* ---------
*
* filterfunc onComplete
* filterfunc onError
* filterfunc onUpdate
* trigger trigger
*
* readonly player from
*
* readonly real timeStarted
* readonly real timeFinished
* readonly real timeElapsed
*
* readonly integer intCount
* readonly integer boolCount
* readonly integer strCount
* readonly integer realCount
* readonly integer playersDone
*
* readonly boolean buffering
*
* readonly static integer last
* readonly static player LocalPlayer
* readonly static boolean Initialized
*
* static method create takes player from returns SyncData
* static method destroy takes nothing returns nothing
* static method gameTime takes nothing returns real
*
* function GetSyncedData takes nothing returns SyncData
*
***************************************************************/
globals
// characters that can be synced (ascii)
private constant string ALPHABET = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
// safe characters for use in game cache keys
// (case sensitive)
private constant string SAFE_KEYS = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`{|}~"
// how fast the buffer updates
private constant real UPDATE_PERIOD = 0.03125
// automatically recycle indices when the syncing player leaves
private constant boolean AUTO_DESTROY_ON_LEAVE = true
// automatically stop buffering when an error occurs
private constant boolean STOP_BUFFERING_ON_ERROR = true
// preload game cache key strings on init
private constant boolean PRELOAD_STR_CACHE = true
// size of the alphabet
private constant integer ALPHABET_BASE = StringLength(ALPHABET)
// stop reading the string buffer when reaching this char
private constant string TERM_CHAR = "`"
// maximum number of strings *per instance*
private constant integer MAX_STRINGS = 8192
// filenames for gc (short names have faster sync time)
private constant string CACHE_FILE = "i.w3v"
private constant string CACHE_FILE_STR = "s.w3v"
// don't edit below this line
constant integer EVENT_SYNC_CACHE = 3
constant integer SYNC_ERROR_TIMEOUT = 1
constant integer SYNC_ERROR_PLAYERLEFT = 2
private SelectionSync Synchronizer
endglobals
//**************************************************************
function GetSyncedData takes nothing returns SyncData
return SyncData(SyncData.Last)
endfunction
public function I2Char takes string alphabet, integer i returns string
return SubString(alphabet, i, i + 1)
endfunction
public function Char2I takes string alphabet, string c returns integer
local integer i = 0
local string s
local integer l = StringLength(alphabet)
loop
set s = I2Char(alphabet, i)
exitwhen i == l
if (s == c) then
return i
endif
set i = i + 1
endloop
return 0
endfunction
public function ConvertBase takes string alphabet, integer i returns string
local integer b
local string s = ""
local integer l = StringLength(alphabet)
if i < l then
return I2Char(alphabet, i)
endif
loop
exitwhen i <= 0
set b = i - ( i / l ) * l
set s = I2Char(alphabet, b) + s
set i = i / l
endloop
return s
endfunction
public function PopulateString takes string s, integer makeLen returns string
local integer i = 0
local integer l = StringLength(s)
if (l == makeLen) then
return s
endif
set l = makeLen-l
loop
exitwhen i > l
set s = s + TERM_CHAR
set i = i + 1
endloop
return s
endfunction
//**************************************************************
globals
// string table keys
private constant integer KEY_STR_POS = (0*MAX_STRINGS)
private constant integer KEY_STR_LEN = (1*MAX_STRINGS)
// pending data storage space
private constant integer KEY_STR_CACHE = (2*MAX_STRINGS)
endglobals
struct SyncData
real timeout
filterfunc onComplete
filterfunc onError
filterfunc onUpdate
trigger trigger
readonly integer lastError
readonly player from
readonly real timeStarted
readonly real timeFinished
readonly real timeElapsed
readonly integer intCount
readonly integer boolCount
readonly integer strCount
readonly integer realCount
readonly integer playersDone
readonly boolean buffering
readonly static boolean Initialized = false
readonly static integer Last = 0
readonly static player LocalPlayer
readonly static integer LocalPlayerID
private static integer Running = 0
private static real timeCounter = 0.00
private static trigger EventTrig = CreateTrigger()
private static hashtable Table
private static hashtable CharTable
private static gamecache array Cache
private static integer array PendingCount
private static timer Elapsed
private static timer BufferTimer
private static integer AlphaHash
private integer strBufferLen
private trigger eventTrig
private string mkey
private boolean localFinished
private thistype next
private thistype prev
static method bool2I takes boolean b returns integer
if (b) then
return 1
endif
return 0
endmethod
private static method hashString takes string c returns integer
return StringHash(I2S(bool2I(StringCase(c, true) == c)) + c)
endmethod
static method char2I takes string alphabet, string c returns integer // requires preloading table with data
return LoadInteger(SyncData.CharTable, .AlphaHash, .hashString(c))
endmethod
private method resetVars takes nothing returns nothing
set this.intCount = 0
set this.strCount = 0
set this.boolCount = 0
set this.realCount = 0
set this.playersDone = 0
set this.strBufferLen = 0
set this.timeStarted = 0
set this.timeFinished = 0
set this.lastError = 0
set this.onComplete = null
set this.onError = null
set this.onUpdate = null
set this.timeout = 0.00
set this.buffering = false
set this.localFinished = false
set this.trigger = null
endmethod
private static method getKey takes integer pos returns string
local string position=""
if (HaveSavedString(Table, KEY_STR_CACHE, pos)) then
return LoadStr(Table, KEY_STR_CACHE, pos)
endif
set position = ConvertBase(SAFE_KEYS, pos)
call SaveStr(Table, KEY_STR_CACHE, pos, position)
return position
endmethod
static method create takes player from returns thistype
local thistype this
// Player has to be playing because of GetLocalPlayer use.
if (GetPlayerController(from) != MAP_CONTROL_USER or GetPlayerSlotState(from) != PLAYER_SLOT_STATE_PLAYING) then
return 0
endif
set this = thistype.allocate()
set this.from = from
set this.mkey = getKey(this-1)
call this.resetVars()
set thistype(0).next.prev = this
set this.next = thistype(0).next
set thistype(0).next = this
set this.prev = 0
return this
endmethod
method refresh takes nothing returns nothing
local integer i = 0
local integer p = 0
loop
static if (LIBRARY_PlayerUtils) then
exitwhen i == User.AmountPlaying
set p = User.fromPlaying(i).id
else
exitwhen i == bj_MAX_PLAYER_SLOTS
set p = i
endif
call RemoveSavedInteger(Table, this, KEY_STR_POS + p)
call RemoveSavedInteger(Table, this, KEY_STR_LEN + p)
call RemoveSavedBoolean(Table, p, this) // playerdone
set i = i + 1
endloop
call FlushStoredMission(Cache[0], this.mkey)
call FlushStoredMission(Cache[1], this.mkey)
call this.resetVars()
endmethod
method destroy takes nothing returns nothing
if (this.eventTrig != null) then
call DestroyTrigger(this.eventTrig)
set this.eventTrig=null
endif
call this.refresh()
set this.next.prev = this.prev
set this.prev.next = this.next
call this.deallocate()
endmethod
method hasInt takes integer index returns boolean
return HaveStoredInteger(Cache[0], this.mkey, getKey(index))
endmethod
method hasReal takes integer index returns boolean
return HaveStoredReal(Cache[0], this.mkey, getKey(index))
endmethod
method hasBool takes integer index returns boolean
return HaveStoredBoolean(Cache[0], this.mkey, getKey(index))
endmethod
method hasString takes integer index returns boolean
local integer i = LoadInteger(Table, this, KEY_STR_POS+index)
if (index > 0 and i == 0) then
return false
endif
return HaveStoredInteger(Cache[1], this.mkey, getKey(i + LoadInteger(Table, this, KEY_STR_LEN+index)))
endmethod
method addInt takes integer i returns nothing
local string position=getKey(intCount)
if (LocalPlayer == this.from) then
call StoreInteger(Cache[0], this.mkey, position, i)
endif
set intCount=intCount+1
endmethod
method addReal takes real i returns nothing
local string position=getKey(realCount)
if (LocalPlayer == this.from) then
call StoreReal(Cache[0], this.mkey, position, i)
endif
set realCount=realCount+1
endmethod
method addBool takes boolean flag returns nothing
local string position=getKey(boolCount)
if (LocalPlayer == this.from) then
call StoreBoolean(Cache[0], this.mkey, position, flag)
endif
set boolCount=boolCount+1
endmethod
// SyncStoredString doesn't work
method addString takes string s, integer length returns nothing
local string position
local integer i = 0
local integer strPos = 0
local integer strLen = 0
if (StringLength(s) < length) then
set s = PopulateString(s, length)
endif
// store the string position in the table
if (strCount == 0) then
call SaveInteger(Table, this, KEY_STR_POS, 0)
else
set strLen = LoadInteger(Table, this, KEY_STR_LEN + (strCount-1)) + 1
set strPos = LoadInteger(Table, this, KEY_STR_POS + (strCount-1)) + strLen
call SaveInteger(Table, this, KEY_STR_POS + strCount, strPos)
endif
// convert each character in the string to an integer
loop
exitwhen i > length
set position = getKey(strPos + i)
if (LocalPlayer == this.from) then
call StoreInteger(Cache[1], this.mkey, position, .char2I(ALPHABET, SubString(s, i, i + 1)))
endif
set i = i + 1
endloop
set strBufferLen = strBufferLen + length
call SaveInteger(Table, this, KEY_STR_LEN+strCount, length) // store the length as well
set strCount=strCount+1
endmethod
method readInt takes integer index returns integer
return GetStoredInteger(Cache[0], this.mkey, getKey(index))
endmethod
method readReal takes integer index returns real
return GetStoredReal(Cache[0], this.mkey, getKey(index))
endmethod
method readBool takes integer index returns boolean
return GetStoredBoolean(Cache[0], this.mkey, getKey(index))
endmethod
method readString takes integer index returns string
local string s = ""
local string c
local integer i = 0
local integer strLen = LoadInteger(Table, this, KEY_STR_LEN+index)
local integer strPos
if (not hasString(index)) then
return null
endif
set strLen = LoadInteger(Table, this, KEY_STR_LEN+index)
set strPos = LoadInteger(Table, this, KEY_STR_POS+index)
loop
exitwhen i > strLen
set c = I2Char(ALPHABET, GetStoredInteger(Cache[1], this.mkey, getKey(strPos + i)))
if (c == TERM_CHAR) then
return s
endif
set s = s + c
set i = i + 1
endloop
return s
endmethod
private method fireListeners takes nothing returns nothing
set Last = this
if (this.eventTrig != null) then
call TriggerEvaluate(this.eventTrig)
endif
if (this.trigger != null and TriggerEvaluate(this.trigger)) then
call TriggerExecute(this.trigger)
endif
endmethod
private method fireEvent takes filterfunc func returns nothing
set Last = this
call TriggerAddCondition(EventTrig, func)
call TriggerEvaluate(EventTrig)
call TriggerClearConditions(EventTrig)
endmethod
method addEventListener takes filterfunc func returns nothing
if (this.eventTrig == null) then
set this.eventTrig = CreateTrigger()
endif
call TriggerAddCondition(this.eventTrig, func)
endmethod
public static method gameTime takes nothing returns real
return timeCounter + TimerGetElapsed(Elapsed)
endmethod
private method error takes integer errorId returns nothing
set this.lastError = errorId
if (this.onError != null) then
call this.fireEvent(this.onError)
endif
call this.fireListeners()
static if (STOP_BUFFERING_ON_ERROR) then
set this.buffering = false
endif
endmethod
private static method readBuffer takes nothing returns nothing
local boolean b = true
local integer i = 0
local thistype data = thistype(0).next
loop
exitwhen data == 0
// find the nearest instance that is still buffering
loop
exitwhen data.buffering or data == 0
set data=data.next
endloop
// if none are found, exit
if (not data.buffering) then
return
endif
set data.timeElapsed = data.timeElapsed + UPDATE_PERIOD
if (data.onUpdate != null) then
call data.fireEvent(data.onUpdate)
endif
if (data.timeout > 0 and data.timeElapsed > data.timeout) then
call data.error(SYNC_ERROR_TIMEOUT)
endif
// if the player has left, destroy the instance
if (GetPlayerSlotState(data.from) != PLAYER_SLOT_STATE_PLAYING) then
call data.error(SYNC_ERROR_PLAYERLEFT)
static if (AUTO_DESTROY_ON_LEAVE) then
call data.destroy()
endif
endif
set b = true
// make sure all integers have been synced
if (data.intCount > 0 and not data.hasInt(data.intCount-1)) then
set b = false
endif
// make sure all reals have been synced
if (data.realCount > 0 and not data.hasReal(data.realCount-1)) then
set b = false
endif
// check strings too
if (data.strCount > 0 and not data.hasString(data.strCount-1)) then
set b = false
endif
// and booleans
if (data.boolCount > 0 and not data.hasBool(data.boolCount-1)) then
set b = false
endif
// if everything has been synced
if (b) then
if (not data.localFinished) then // async
set data.localFinished = true
// notify everyone that the local player has recieved all of the data
call Synchronizer.syncValue(LocalPlayer, data)
endif
endif
set data = data.next
endloop
endmethod
method startChunk takes integer i, integer end returns nothing
local integer n = 0
local integer j = 0
local integer p = 0
local string position
// Begin syncing
loop
exitwhen i > end
set position = LoadStr(Table, KEY_STR_CACHE, i)
if (i < intCount and LocalPlayer == this.from) then
call SyncStoredInteger(Cache[0], this.mkey, position)
endif
if (i < realCount and LocalPlayer == this.from) then
call SyncStoredReal(Cache[0], this.mkey, position)
endif
if (i < boolCount and LocalPlayer == this.from) then
call SyncStoredBoolean(Cache[0], this.mkey, position)
endif
if (i < strCount and LocalPlayer == this.from) then
set n = LoadInteger(Table, this, KEY_STR_LEN + i)
set p = LoadInteger(Table, this, KEY_STR_POS + i)
set j = 0
loop
exitwhen j > n
set position = LoadStr(Table, KEY_STR_CACHE, p + j)
if (LocalPlayer == this.from) then
call SyncStoredInteger(Cache[1], this.mkey, position)
endif
set j = j + 1
endloop
endif
set i = i + 1
endloop
if (this.timeStarted != 0.00) then
return
endif
set this.timeStarted = gameTime()
set this.playersDone = 0
set this.buffering = true
set this.timeElapsed = (UPDATE_PERIOD - TimerGetElapsed(BufferTimer)) * -1
if (Running==0) then
call TimerStart(BufferTimer, UPDATE_PERIOD, true, function thistype.readBuffer)
endif
set Running=Running+1
endmethod
method start takes nothing returns nothing
local integer l = intCount
// Find the highest count
if (l < realCount) then
set l = realCount
endif
if (l < strCount) then
set l = strCount
endif
if (l < boolCount) then
set l = boolCount
endif
call startChunk(0, l)
endmethod
method isPlayerIdDone takes integer pid returns boolean
return LoadBoolean(Table, pid, this)
endmethod
method isPlayerDone takes player p returns boolean
return isPlayerIdDone(GetPlayerId(p))
endmethod
private static method updateStatus takes nothing returns boolean
local integer i = 0
local integer p = GetSyncedPlayerId()
local boolean b = true
local boolean c = true
local thistype data = GetSyncedInteger()
local triggercondition tc
if (GetSyncedInstance() != Synchronizer or not data.buffering) then
return false
endif
set data.playersDone = data.playersDone + 1
call SaveBoolean(Table, p, data, true) // set playerdone
// check if everyone has received the data
loop
static if (LIBRARY_PlayerUtils) then
exitwhen i == User.AmountPlaying
set p = User.fromPlaying(i).id
set c = User.fromPlaying(i).isPlaying
else
exitwhen i == bj_MAX_PLAYER_SLOTS
set p = i
set c = (GetPlayerController(Player(p)) == MAP_CONTROL_USER and GetPlayerSlotState(Player(p)) == PLAYER_SLOT_STATE_PLAYING)
endif
if (c and not data.isPlayerIdDone(p)) then
set b = false // someone hasn't
endif
set i = i + 1
endloop
// if everyone has recieved the data
if (b) then
set Running = Running-1
if (Running == 0) then
call PauseTimer(BufferTimer)
endif
set data.buffering = false
set data.timeFinished = gameTime()
set data.timeElapsed = data.timeFinished - data.timeStarted
// fire events
if (data.onComplete != null) then
call data.fireEvent(data.onComplete)
endif
call data.fireListeners()
call SyncInteger_FireEvents(EVENT_SYNC_CACHE)
endif
return false
endmethod
private static method trackTime takes nothing returns nothing
set timeCounter = timeCounter + 10
endmethod
private static method preloadChar2I takes nothing returns nothing
local integer i = 0
local string c
set .AlphaHash = .hashString(ALPHABET)
loop
exitwhen i >= ALPHABET_BASE
set c = I2Char(ALPHABET, i)
call SaveInteger(SyncData.CharTable, .AlphaHash, .hashString(c), Char2I(ALPHABET, c))
set i = i + 1
endloop
endmethod
private static method onInit takes nothing returns nothing
set Synchronizer = SelectionSync.create()
set Table = InitHashtable()
set CharTable = InitHashtable()
set Cache[0] = InitGameCache(CACHE_FILE)
set Cache[1] = InitGameCache(CACHE_FILE_STR)
set Elapsed = CreateTimer()
set BufferTimer = CreateTimer()
static if (LIBRARY_PlayerUtils) then
set LocalPlayer = User.Local
set LocalPlayerID = User.fromLocal().id
else
set LocalPlayer = GetLocalPlayer()
set LocalPlayerID = GetPlayerId(LocalPlayer)
endif
call OnSyncInteger(Filter(function thistype.updateStatus))
call TimerStart(Elapsed, 10., true, function thistype.trackTime)
static if (PRELOAD_STR_CACHE) then
loop
exitwhen Last == ALPHABET_BASE
call getKey(Last)
set Last = Last + 1
endloop
set Last = 0
endif
call preloadChar2I()
set Initialized = true
endmethod
endstruct
endlibrary
library SyncInteger uses optional UnitDex /*or any unit indexer*/, optional GroupUtils, optional xebasic, optional PlayerUtils
/***************************************************************
*
* v1.2.0, by TriggerHappy
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*
* This library allows you to send integers to all other players.
*
* _________________________________________________________________________
* 1. Installation
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Copy the script to your map and save it (requires JassHelper *or* JNGP)
* _________________________________________________________________________
* 2. How it works
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* 1. Creates {DUMMY_COUNT} units and assigns {BASE} of them an integer from 0-{BASE}.
* The 2nd to last dummy is used to signal when the sequence of numbers is over and
* the last dummy signifies a negative number.
*
* 2. Breaks down the number you want to sync to one or more {BASE} integers,
* then selects each dummy unit assoicated with that integer.
*
* 4. The selection event fires for all players when the selection has been sycned
*
* 5. The ID of the selected unit is one of the {BASE} numbers. The current
* total (starts at 0) is multiplied by {BASE} and the latest synced integer is
* added to that. The process will repeat until it selects the 2nd to last dummy,
* and the total is our result.
* _________________________________________________________________________
* 3. Proper Usage
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* - Avoid the SyncSelections native. It may cause the thread to hang or
* make some units un-able to move.
*
* - Dummies must be select-able (no locust)
*
* - Run the script in debug mode while testing
*
* _________________________________________________________________________
* 4. Struct API
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* struct SelectionSync
*
* static method create takes nothing returns thistype
*
* method syncValue takes player p, integer number returns boolean
* method destroy takes nothing returns nothing
* _________________________________________________________________________
* 5. Function API
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* function SyncInteger takes player p, integer number returns boolean
*
* function GetSyncedInteger takes nothing returns integer
* function GetSyncedInstance takes nothing returns integer
* function GetSyncedPlayer takes nothing returns player
* function GetSyncedPlayerId takes nothing returns integer
* function IsPlayerSyncing takes player p returns boolean
* function IsPlayerIdSyncing takes integer pid returns boolean
* function IsSyncEnabled takes nothing returns boolean
* function SyncIntegerToggle takes boolean flag returns nothing
* function SyncIntegerEnable takes nothing returns nothing
* function SyncIntegerDisable takes nothing returns nothing
*
* function OnSyncInteger takes code func returns triggercondition
* function RemoveSyncEvent takes triggercondition action returns nothing
* function TriggerRegisterSyncEvent takes trigger t, integer eventtype returns nothing
*
* function SyncTerminate takes boolean destroyEvent returns nothing
*
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* -http://www.hiveworkshop.com/threads/syncinteger.278674/
*
*/
globals
// create a struct instance for global use
public constant boolean DEFAULT_INSTANCE = true
// owner of the dummy units
public constant player DUMMY_PLAYER = Player(PLAYER_NEUTRAL_PASSIVE)
// dummy can *not* have locust (must be selectabe)
// basically anything should work (like 'hfoo')
public constant integer DUMMY_ID = 'hfoo' // XE_DUMMY_UNITID
// dummy ghost ability
public constant integer DUMMY_ABILITY = 'Aeth'
// allow debug messages (also requries JassHelper Debug Mode)
public constant boolean ALLOW_DEBUGGING = true
// higher == more dummies but less selections (faster)
public constant integer BASE = 10
// two higher than BASE (jasshelper doesn't allow BASE + 2)
public constant integer DUMMY_COUNT = 12
// endconfig
constant integer EVENT_SYNC_INTEGER = 1
public integer DefaultInstance = 0
private trigger OnSelectTrigger = CreateTrigger()
private trigger EventTrig = CreateTrigger()
private real FireEvent = 0
private group SelectionGroup
private integer LastPlayer
private integer LastSync
private integer LastInstance
private player LocalPlayer
private integer array ActiveSyncs
private real DUMMY_X = 0
private real DUMMY_Y = 0
private integer array DummyInstance
endglobals
function GetSyncedInteger takes nothing returns integer
return LastSync
endfunction
function GetSyncedPlayer takes nothing returns player
return Player(LastPlayer)
endfunction
function GetSyncedInstance takes nothing returns integer
return LastInstance
endfunction
function GetSyncedPlayerId takes nothing returns integer
return LastPlayer
endfunction
function IsPlayerIdSyncing takes integer pid returns boolean
return ActiveSyncs[pid] > 0
endfunction
function IsPlayerSyncing takes player p returns boolean
return ActiveSyncs[GetPlayerId(p)] > 0
endfunction
function IsSyncEnabled takes nothing returns boolean
return IsTriggerEnabled(OnSelectTrigger)
endfunction
function SyncIntegerEnable takes nothing returns nothing
call EnableTrigger(OnSelectTrigger)
endfunction
function SyncIntegerDisable takes nothing returns nothing
call DisableTrigger(OnSelectTrigger)
endfunction
function SyncIntegerToggle takes boolean flag returns nothing
if (flag) then
call EnableTrigger(OnSelectTrigger)
else
call DisableTrigger(OnSelectTrigger)
endif
endfunction
function OnSyncInteger takes filterfunc func returns triggercondition
return TriggerAddCondition(EventTrig, func)
endfunction
function RemoveSyncEvent takes triggercondition action returns nothing
call TriggerRemoveCondition(EventTrig, action)
endfunction
function TriggerRegisterSyncEvent takes trigger t, integer eventtype returns nothing
call TriggerRegisterVariableEvent(t, SCOPE_PREFIX + "FireEvent", EQUAL, eventtype)
endfunction
public function FireEvents takes real eventtype returns nothing
set FireEvent = eventtype
set FireEvent = 0
endfunction
// This function is called when a unit is selected.
private function OnSelect takes nothing returns boolean
local unit u = GetTriggerUnit()
local player p = GetTriggerPlayer()
local integer id = GetPlayerId(p)
local integer udata = GetUnitUserData(u)
local SelectionSync this = DummyInstance[udata]
local boolean isNeg = (this.Dummy[DUMMY_COUNT-1] == u)
local integer index = this.DummyID[udata] - 1
if (this <= 0) then
return false
endif
if (isNeg) then
set this.SyncingValue[id] = this.SyncingValue[id]*-1
endif
if (isNeg or this.Dummy[DUMMY_COUNT-2] == u) then
set ActiveSyncs[id] = ActiveSyncs[id] - 1
// The number is finished syncing, fire the events
set LastPlayer = id
set LastSync = this.SyncingValue[id]
set LastInstance = this
set FireEvent = EVENT_SYNC_INTEGER
call TriggerEvaluate(EventTrig)
// Reset variables
set FireEvent = 0
set this.SyncingValue[id] = -1
else
if (this.SyncingValue[id] == -1) then
set this.SyncingValue[id] = 0
endif
// Build the number we are trying to sync
set this.SyncingValue[id] = this.SyncingValue[id] * BASE + index
endif
set u = null
return false
endfunction
private keyword SyncIntegerInit
// This struct allows us to dynamically create a group of units
// which we can use to synchronize our integer through unit selections.
struct SelectionSync
public unit array Dummy[DUMMY_COUNT]
public integer array DummyID[DUMMY_COUNT]
public integer array SyncingValue[12]
public static method debugger takes boolean b, string s returns nothing
static if (ALLOW_DEBUGGING and DEBUG_MODE) then
if (b) then
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "|c00FF0000" + SCOPE_PREFIX + s + "|r")
endif
endif
endmethod
static method create takes nothing returns thistype
local thistype this = thistype.allocate()
local integer i = 0
local integer uid
debug call .debugger(OnSelectTrigger == null, "[SelectionSync.create()] OnSelectTrigger is null and has no events attached to it.")
debug call .debugger(this.Dummy[0] != null, "[SelectionSync.create()] Dummy not null!")
loop
exitwhen i >= DUMMY_COUNT
set this.Dummy[i] = CreateUnit(DUMMY_PLAYER, DUMMY_ID, DUMMY_X, DUMMY_Y, i)
set uid = GetUnitUserData(this.Dummy[i])
if (uid == 0) then
set uid = ( (this-1) * DUMMY_COUNT ) + (i + 1)
call SetUnitUserData(this.Dummy[i], uid)
endif
debug call .debugger((i == 0) and (this.Dummy[i] == null), "[SelectionSync.create()] Dummy unit is null (check DUMMY_ID).")
debug call .debugger((i == 0) and (GetUnitAbilityLevel(this.Dummy[i], 'Aloc') > 0), "[SelectionSync.create()] Dummy units must be selectable (detected locust).")
set this.DummyID[uid] = i + 1
set DummyInstance[uid] = this
call UnitAddAbility(this.Dummy[i], DUMMY_ABILITY)
call PauseUnit(this.Dummy[i], true)
call SetUnitScale(this.Dummy[i], 0, 0, 0)
set i = i + 1
endloop
return this
endmethod
method syncValue takes player p, integer number returns boolean
local integer x = number
local integer i = 0
local integer d = BASE
local integer j = 0
local integer n = 0
local integer l = 0
local integer playerId = GetPlayerId(p)
local unit u
local unit last
debug call .debugger(OnSelectTrigger == null, "[SelectionSync.syncValue()] OnSelectTrigger is destroyed.")
debug call .debugger(IsSyncEnabled() == false, "[SelectionSync.syncValue()] OnSelectTrigger is disabled.")
if (not IsSyncEnabled()) then
return false
endif
// Check if the number is negative
if (number < 0) then
set d = DUMMY_COUNT-1
set number = number * -1
endif
loop
set x = x/(BASE)
exitwhen x==0
set i=i+1
endloop
// Count how many units are selected
call GroupEnumUnitsSelected(SelectionGroup, p, null)
set bj_groupCountUnits = 0
set u = FirstOfGroup(SelectionGroup)
loop
exitwhen u == null
set last = u
call GroupRemoveUnit(SelectionGroup, u)
set bj_groupCountUnits = bj_groupCountUnits + 1
set u = FirstOfGroup(SelectionGroup)
endloop
// If the queue is full, de-select the last unit which
// will allow us to select a dummy, and hopefully
// avoid a flickering effect.
if (bj_groupCountUnits >= 12 and LocalPlayer == p) then
call SelectUnit(last, false)
endif
set j=R2I(Pow(BASE, i))
loop
set n = j
set x = number/n
set j = j / BASE
if (LocalPlayer == p) then
call SelectUnit(this.Dummy[x], true)
call SelectUnit(this.Dummy[x], false)
endif
set number = number-x*n
exitwhen i == 0
set i = i - 1
endloop
if (LocalPlayer == p) then
call SelectUnit(this.Dummy[d], true)
call SelectUnit(this.Dummy[d], false)
if (bj_groupCountUnits >= 12) then
call SelectUnit(last, true)
endif
endif
set u = null
set last = null
set ActiveSyncs[playerId] = ActiveSyncs[playerId] + 1
return true
endmethod
method destroy takes nothing returns nothing
local integer i = 0
loop
exitwhen i >= DUMMY_COUNT
call RemoveUnit(this.Dummy[i])
set this.Dummy[i] = null
set i = i + 1
endloop
endmethod
implement SyncIntegerInit
endstruct
function SyncInteger takes player p, integer number returns boolean
debug call SelectionSync.debugger(DefaultInstance == 0, "[SyncInteger()] DefaultInstance is not initialized (make sure DEFAULT_INSTANCE is true")
return SelectionSync(DefaultInstance).syncValue(p, number)
endfunction
function SyncTerminate takes boolean destroyEvents returns boolean
local integer i = 0
if (OnSelectTrigger == null and EventTrig == null) then
return false
endif
if (destroyEvents) then
call DestroyTrigger(OnSelectTrigger)
call DestroyTrigger(EventTrig)
set OnSelectTrigger = null
set EventTrig = null
static if not LIBRARY_GroupUtils then
call DestroyGroup(SelectionGroup)
set SelectionGroup = null
endif
else
call SyncIntegerDisable()
endif
if (DefaultInstance > 0) then
call SelectionSync(DefaultInstance).destroy()
endif
return true
endfunction
//===========================================================================
private module SyncIntegerInit
private static method onInit takes nothing returns nothing
local integer i = 0
local integer j
loop
call TriggerRegisterPlayerUnitEvent(OnSelectTrigger, Player(i), EVENT_PLAYER_UNIT_SELECTED, null)
set i = i + 1
exitwhen i==bj_MAX_PLAYER_SLOTS
endloop
call TriggerAddCondition(OnSelectTrigger, Filter(function OnSelect))
static if (LIBRARY_GroupUtils) then
set SelectionGroup=ENUM_GROUP
else
set SelectionGroup=CreateGroup()
endif
static if (LIBRARY_PlayerUtils) then
set LocalPlayer=User.Local
else
set LocalPlayer=GetLocalPlayer()
endif
set DUMMY_X = GetCameraBoundMaxX() + 2000
set DUMMY_Y = GetCameraBoundMaxY() + 2000
static if (DEFAULT_INSTANCE) then
set DefaultInstance = SelectionSync.create()
endif
endmethod
endmodule
endlibrary
// few modifications by triggerhappy
library FileIO /* v2.0.0.1
*************************************************************************************
*
* Used read/write data to/from files
*
************************************************************************************
*
* struct File extends array
*
* Description
* -----------------------
*
* This is used to read data from files and write data to files
*
* Creators/Destructors
* -----------------------
*
* static method open takes string mapName, string fileName, integer flag returns File
* - Used to open a file. Pass read/write flag in to open file for reading or writing.
*
* method close takes nothing returns nothing
*
* Fields
* -----------------------
*
* readonly boolean enabled
* - This is a local value. It is true if the player can read files, false if the player can't read files.
* readonly string localFileScriptName
* - This contains the name and path to a bat file that is automatically created
* - on the player's computer in the event that they can't read files. Output this filename
* - and tell them to run the script so that they can read files.
*
* integer Flag.READ
* integer Flag.WRITE
* - These are the read/write flags. Pass these into File.open
*
* Methods
* -----------------------
*
* method read takes nothing returns string
* - Reads next line from file. Returns null when there are no more lines.
*
* method write takes string data returns nothing
* - Writes line to file. The only limit is wc3 max string size.
*
************************************************************************************/
//Tests if the player can read files
private module LocalFileTestInit
private static method onInit takes nothing returns nothing
call TimerStart(CreateTimer(),0,false,function thistype.init)
endmethod
endmodule
private struct LocalFileTest extends array
//private static constant string FLAG_FOLDER = "Flag"
private static constant string FLAG_FILE = "flag.pld"
readonly static boolean success = false
private static method testForLocalEnabled takes nothing returns nothing
local string playerName = GetPlayerName(GetLocalPlayer())
call Preloader(FLAG_FILE)
set success = GetPlayerName(GetLocalPlayer()) != playerName
call SetPlayerName(GetLocalPlayer(), playerName)
endmethod
private static method writeLocalFileTest takes nothing returns nothing
call PreloadGenClear()
call PreloadGenStart()
call Preload("\")\r\n\tcall SetPlayerName(GetLocalPlayer(), \"FLAG TEST LOCAL CHECK\")\r\n//")
call Preload("\" )\r\nendfunction\r\nfunction AAA takes nothing returns nothing \r\n//")
call PreloadGenEnd(FLAG_FILE)
endmethod
private static method writeEnableLocalRegistry takes string fname returns nothing
call PreloadGenClear()
call PreloadGenStart()
call Preload("\")\r\necho Set Reg = CreateObject(\"wscript.shell\") > download.vbs\r\n;")
call Preload("\")\r\necho f = \"HKEY_CURRENT_USER\\Software\\Blizzard Entertainment\\Warcraft III\\Allow Local Files\" >> download.vbs\r\n;")
call Preload("\")\r\necho f = Replace(f,\"\\\",Chr(92)) >> download.vbs\r\n;") //"
call Preload("\")\r\necho Reg.RegWrite f, 1, \"REG_DWORD\" >> download.vbs\r\n;")
call Preload("\")\r\nstart download.vbs\r\n;")
call PreloadGenEnd(fname)
endmethod
private static method init takes nothing returns nothing
call DestroyTimer(GetExpiredTimer())
call writeLocalFileTest()
call testForLocalEnabled()
if (not success) then
call writeEnableLocalRegistry(File.localFileScriptName)
call writeEnableLocalRegistry("C:\\!! AllowLocalFiles\\" + File.localFileScriptName) // for pre 128
//call writeEnableLocalRegistry("C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\" + File.localFileScriptName)
endif
endmethod
implement LocalFileTestInit
endstruct
private struct FlagG extends array
static constant integer READ = 1
static constant integer WRITE = 2
endstruct
private module FileInit
private static method onInit takes nothing returns nothing
call init()
endmethod
endmodule
struct File extends array
private static constant integer MAX_LINE_LENGTH = 209 //max amount of data per line
private static hashtable stringTable //stores lines from file, 16 lines per file
private static integer instanceCount = 0
private static integer array recycler
private string mapName
private string fileName
private string data //data buffer
private integer fileIndex //current file index, -2147483648 to 2147483647
private integer dataIndex //current data index for file, 0-15
private integer flag //read/write flag
private static string array extra0 //extra 0s on string lengths to make digits = 4
static method operator Flag takes nothing returns FlagG
return 0
endmethod
static method operator enabled takes nothing returns boolean
return LocalFileTest.success
endmethod
static method operator localFileScriptName takes nothing returns string
return "AllowLocalFiles.bat.txt"
endmethod
static method operator localFileScriptNameFull takes nothing returns string
return localFileScriptName
endmethod
static method open takes string mapName, string fileName, integer flag returns File
local thistype this = recycler[0]
if (0 == this) then
set this = instanceCount + 1
set instanceCount = this
else
set recycler[0] = recycler[this]
endif
set fileIndex = -2147483648
set this.mapName = mapName
set this.fileName = fileName
set this.flag = flag
if (flag == Flag.READ) then
//if reading, go to previous file index so that the reader can auto load up the file
set fileIndex = fileIndex - 1
set dataIndex = 16
elseif (flag == Flag.WRITE) then
//open file for writing
call PreloadGenClear()
call PreloadGenStart()
endif
return this
endmethod
//loads file and returns lines out of file
private static string array pname
private method loadline takes nothing returns nothing
local integer i
//if there are no more lines in the file, load next file
if (16 == dataIndex) then
set dataIndex = 0
set fileIndex = fileIndex + 1
//store current player names
set i = 15
loop
set pname[i] = GetPlayerName(Player(i))
exitwhen 0 == i
set i = i - 1
endloop
//load file (sets the player names to lines in file)
call Preloader(mapName + "_" + fileName)
//flush file buffer
call FlushChildHashtable(stringTable, this)
//load lines from file to file buffer and return player names to normal
set i = 15
loop
if (pname[i] != GetPlayerName(Player(i))) then
call SaveStr(stringTable, this, i, SubString(GetPlayerName(Player(i)), 1, StringLength(GetPlayerName(Player(i)))))
call SetPlayerName(Player(i), pname[i])
endif
exitwhen 0 == i
set i = i - 1
endloop
endif
//add next line of file to data
set this.data = this.data + LoadStr(stringTable, this, dataIndex)
set dataIndex = dataIndex + 1
endmethod
method read takes nothing returns string
local integer length
local string data = ""
debug if (flag != Flag.READ) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"FILE IO ERROR: ATTEMPT TO READ TO FILE OPEN FOR WRITING")
debug return null
debug endif
//if there is no data at the moment, get next line from file
if (StringLength(this.data) < 4) then
call loadline()
//if there is no next line, return null
if (StringLength(this.data) < 4) then
return null
endif
endif
//get the length of the data (# of characters that make it up)
set length = S2I(SubString(this.data, 0, 4))
set this.data = SubString(this.data, 4, StringLength(this.data))
loop
if (length > StringLength(this.data)) then
//if the length is greater than the data currently in the buffer, dump the
//entire buffer to the data being returned and get the next line
set length = length - StringLength(this.data)
set data = data + this.data
set this.data = ""
call loadline()
else
//if the length is less than the data in the buffer, dump that data
//from the buffer
set data = data + SubString(this.data, 0, length)
set this.data = SubString(this.data, length, StringLength(this.data))
set length = 0
return data
endif
endloop
return null
endmethod
method write takes string data returns nothing
local integer length = StringLength(data)
local integer digits = 0
local boolean done = false
debug if (flag != Flag.WRITE) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"FILE IO ERROR: ATTEMPT TO WRITE TO FILE OPEN FOR READING")
debug return
debug endif
//first, retrieve the # of digits for the length
loop
set digits = digits + 1
set length = length/10
exitwhen 0 == length
endloop
//add the extra digits to and length to buffer
set this.data = this.data + extra0[digits] + I2S(StringLength(data))
loop
if (StringLength(data) > 400) then
//if the length of the data is greater than 400, throw first 400 chars into buffer
set this.data = this.data + SubString(data, 0, 400)
set data = SubString(data, 400, StringLength(data))
else
//if the length isn't greater than 400, throw it all into the buffer
set this.data = this.data + data
set data = ""
set done = true
endif
loop
//throw the data into file in sets of MAX_LINE_LENGTH chars until there is not
//enough data in the buffer to completely fill a line
exitwhen StringLength(this.data) < MAX_LINE_LENGTH
call Preload("\")\r\n\tcall SetPlayerName(Player("+I2S(dataIndex)+"), \" "+SubString(this.data, 0, MAX_LINE_LENGTH)+"\")\r\n//")
set this.data = SubString(this.data, MAX_LINE_LENGTH, StringLength(this.data))
set dataIndex = dataIndex + 1
if (dataIndex == 16) then
//start a new file
set dataIndex = 0
call Preload( "\" )\r\nendfunction\r\nfunction AAA takes nothing returns nothing \r\n//")
call PreloadGenEnd(mapName + "_" + fileName)
set fileIndex = fileIndex + 1
call PreloadGenClear()
call PreloadGenStart()
endif
endloop
exitwhen done
endloop
endmethod
method close takes nothing returns nothing
if (flag == Flag.READ) then
//flush the read buffer
call FlushChildHashtable(stringTable, this)
elseif (flag == Flag.WRITE) then
//write remaining of data to file and close it out
call Preload("\")\r\n\tcall SetPlayerName(Player("+I2S(dataIndex)+"), \" "+data+"\")\r\n//")
call Preload( "\" )\r\nendfunction\r\nfunction AAA takes nothing returns nothing \r\n//")
call PreloadGenEnd(mapName + "_" + fileName)
endif
//deallocate and reset
set recycler[this] = recycler[0]
set recycler[0] = this
set dataIndex = 0
set data = ""
endmethod
private static method init takes nothing returns nothing
set extra0[1] = "000"
set extra0[2] = "00"
set extra0[3] = "0"
set extra0[4] = ""
set stringTable = InitHashtable()
endmethod
implement FileInit
endstruct
endlibrary
library GameDataWrapper uses FileIO
globals
private constant string MEMHAX_FILENAME = "./MapData.ini"
endglobals
private constant function GetMapName takes nothing returns string
return udg_MapName
endfunction
constant function MemoryHacksEnabled takes nothing returns boolean
static if LIBRARY_MemoryHacks then
return true
else
return false
endif
endfunction
struct GameData
static method write takes string skey, string value returns nothing
static if LIBRARY_MemoryHacks then
call WriteStringToFile(MEMHAX_FILENAME, GetMapName(), skey, value)
else
local File f = File.open(GetMapName(), skey + ".pld", File.Flag.WRITE)
call f.write(value)
call f.close()
endif
endmethod
static method read takes string skey returns string
static if LIBRARY_MemoryHacks then
return ReadStringFromFile(MEMHAX_FILENAME, GetMapName(), skey, "")
else
local File f = File.open(GetMapName(), skey + ".pld", File.Flag.READ)
local string contents = f.read()
call f.close()
return contents
endif
endmethod
static method delete takes string skey returns nothing
call write(skey, "")
endmethod
endstruct
endlibrary
library Savecode requires BigNum
private constant function uppercolor takes nothing returns string
return "|cffff0000"
endfunction
private constant function lowercolor takes nothing returns string
return "|cff00ff00"
endfunction
private constant function numcolor takes nothing returns string
return "|cff0000ff"
endfunction
private function player_charset takes nothing returns string
return "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
endfunction
private function player_charsetlen takes nothing returns integer
return StringLength(player_charset())
endfunction
private function charset takes nothing returns string
return "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
endfunction
private function charsetlen takes nothing returns integer
return StringLength(charset())
endfunction
private function BASE takes nothing returns integer
return charsetlen()
endfunction
private constant function HASHN takes nothing returns integer
return 5000 //1./HASHN() is the probability of a random code being valid
endfunction
private constant function MAXINT takes nothing returns integer
return 2147483647
endfunction
private function player_chartoi takes string c returns integer
local integer i = 0
local string cs = player_charset()
local integer len = player_charsetlen()
loop
exitwhen i>=len or c == SubString(cs,i,i+1)
set i = i + 1
endloop
return i
endfunction
private function chartoi takes string c returns integer
local integer i = 0
local string cs = charset()
local integer len = charsetlen()
loop
exitwhen i>=len or c == SubString(cs,i,i+1)
set i = i + 1
endloop
return i
endfunction
private function itochar takes integer i returns string
return SubString(charset(),i,i+1)
endfunction
//You probably want to use a different char set for this
//Also, use a hash that doesn't suck so much
private function scommhash takes string s returns integer
local integer array count
local integer i = 0
local integer len = StringLength(s)
local integer x
set s = StringCase(s,true)
loop
exitwhen i >= len
set x = player_chartoi(SubString(s,i,i+1))
set count[x] = count[x] + 1
set i = i + 1
endloop
set i = 0
set len = player_charsetlen()
set x = 0
loop
exitwhen i>= len
set x = count[i]*count[i]*i+count[i]*x+x+199
// call BJDebugMsg(I2S(x)+" "+I2S(count[i]))
// call TriggerSleepAction(0.)
set i = i + 1
endloop
if x < 0 then
set x = -x
endif
return x
endfunction
private function modb takes integer x returns integer
if x >= BASE() then
return x - BASE()
elseif x < 0 then
return x + BASE()
else
return x
endif
endfunction
struct Savecode
real digits //logarithmic approximation
BigNum bignum
static method create takes nothing returns Savecode
local Savecode sc = Savecode.allocate()
set sc.digits = 0.
set sc.bignum = BigNum.create(BASE())
return sc
endmethod
method onDestroy takes nothing returns nothing
call .bignum.destroy()
endmethod
method Encode takes integer val, integer max returns nothing
set .digits = .digits + log(max+1,BASE())
call .bignum.MulSmall(max+1)
call .bignum.AddSmall(val)
endmethod
method Decode takes integer max returns integer
return .bignum.DivSmall(max+1)
endmethod
method IsEmpty takes nothing returns boolean
return .bignum.IsZero()
endmethod
method Length takes nothing returns real
return .digits
endmethod
method Clean takes nothing returns nothing
call .bignum.Clean()
endmethod
//These functions get too intimate with BigNum_l
method Pad takes nothing returns nothing
local BigNum_l cur = .bignum.list
local BigNum_l prev
local integer maxlen = R2I(1.0 + .Length())
loop
exitwhen cur == 0
set prev = cur
set cur = cur.next
set maxlen = maxlen - 1
endloop
loop
exitwhen maxlen <= 0
set prev.next = BigNum_l.create()
set prev = prev.next
set maxlen = maxlen - 1
endloop
endmethod
method ToString takes nothing returns string
local BigNum_l cur = .bignum.list
local string s = ""
loop
exitwhen cur == 0
set s = itochar(cur.leaf) + s
set cur = cur.next
endloop
return s
endmethod
method FromString takes string s returns nothing
local integer i = StringLength(s)-1
local BigNum_l cur = BigNum_l.create()
set .bignum.list = cur
loop
set cur.leaf = chartoi(SubString(s,i,i+1))
exitwhen i <= 0
set cur.next = BigNum_l.create()
set cur = cur.next
set i = i - 1
endloop
endmethod
method Hash takes nothing returns integer
local integer hash = 0
local integer x
local BigNum_l cur = .bignum.list
loop
exitwhen cur == 0
set x = cur.leaf
set hash = ModuloInteger(hash+79*hash/(x+1) + 293*x/(1+hash - (hash/BASE())*BASE()) + 479,HASHN())
set cur = cur.next
endloop
return hash
endmethod
//this is not cryptographic which is fine for this application
//sign = 1 is forward
//sign = -1 is backward
method Obfuscate takes integer key, integer sign returns nothing
local integer seed = GetRandomInt(0,MAXINT())
local integer advance
local integer x
local BigNum_l cur = .bignum.list
if sign == -1 then
call SetRandomSeed(.bignum.LastDigit())
set cur.leaf = modb(cur.leaf + sign*GetRandomInt(0,BASE()-1))
set x = cur.leaf
endif
call SetRandomSeed(key)
loop
exitwhen cur == 0
if sign == -1 then
set advance = cur.leaf
endif
set cur.leaf = modb(cur.leaf + sign*GetRandomInt(0,BASE()-1))
if sign == 1 then
set advance = cur.leaf
endif
set advance = advance + GetRandomInt(0,BASE()-1)
call SetRandomSeed(advance)
set x = cur.leaf
set cur = cur.next
endloop
if sign == 1 then
call SetRandomSeed(x)
set .bignum.list.leaf = modb(.bignum.list.leaf + sign*GetRandomInt(0,BASE()-1))
endif
call SetRandomSeed(seed)
endmethod
method Dump takes nothing returns nothing
local BigNum_l cur = .bignum.list
local string s = ""
set s = "max: "+R2S(.digits)
loop
exitwhen cur == 0
set s = I2S(cur.leaf)+" "+s
set cur = cur.next
endloop
call BJDebugMsg(s)
endmethod
method Save takes player p, integer loadtype returns string
local integer key = scommhash(GetPlayerName(p))+loadtype*73
local string s
local integer hash
call .Clean()
set hash = .Hash()
call .Encode(hash,HASHN())
call .Clean()
/////////////////////// Save code information. Comment out next two lines in implementation
//call BJDebugMsg("Expected length: " +I2S(R2I(1.0+.Length())))
//call BJDebugMsg("Room left in last char: "+R2S(1.-ModuloReal((.Length()),1)))
///////////////////////
call .Pad()
call .Obfuscate(key,1)
return .ToString()
endmethod
method Load takes player p, string s, integer loadtype returns boolean
local integer ikey = scommhash(GetPlayerName(p))+loadtype*73
local integer inputhash
call .FromString(s)
call .Obfuscate(ikey,-1)
set inputhash = .Decode(HASHN())
call .Clean()
return inputhash == .Hash()
endmethod
endstruct
private function isupper takes string c returns boolean
return c == StringCase(c,true)
endfunction
private function ischar takes string c returns boolean
return S2I(c) == 0 and c!= "0"
endfunction
private function chartype takes string c returns integer
if(ischar(c)) then
if isupper(c) then
return 0
else
return 1
endif
else
return 2
endif
endfunction
private function testchar takes string c returns nothing
if(ischar(c)) then
if isupper(c) then
call BJDebugMsg(c+" isupper")
else
call BJDebugMsg(c+" islower")
endif
else
call BJDebugMsg(c+ " isnumber")
endif
endfunction
public function colorize takes string s returns string
local string out = ""
local integer i = 0
local integer len = StringLength(s)
local integer ctype
local string c
loop
exitwhen i >= len
set c = SubString(s,i,i+1)
set ctype = chartype(c)
if ctype == 0 then
set out = out + uppercolor()+c+"|r"
elseif ctype == 1 then
set out = out + lowercolor()+c+"|r"
else
set out = out + numcolor()+c+"|r"
endif
set i = i + 1
endloop
return out
endfunction
private function prop_Savecode takes nothing returns boolean
local string s
local Savecode loadcode
//--- Data you want to save ---
local integer medal1 = 10
local integer medal2 = 3
local integer medalmax = 13
local integer XP = 1337
local integer XPmax = 1000000
local Savecode savecode = Savecode.create()
call SetPlayerName(Player(0),"yomp")
call SetPlayerName(Player(1),"fruitcup")
call savecode.Encode(medal1,medalmax)
call savecode.Encode(medal2,medalmax)
call savecode.Encode(XP,XPmax)
//--- Savecode_save generates the savecode for a specific player ---
set s = savecode.Save(Player(0),1)
call savecode.destroy()
// call BJDebugMsg("Savecode: " + Savecode_colorize(s))
//--- User writes down code, inputs again ---
set loadcode = Savecode.create()
if loadcode.Load(Player(0),s,1) then
// call BJDebugMsg("load ok")
else
call BJDebugMsg("load failed")
return false
endif
//Must decode in reverse order of encodes
// load object : max value that data can take
if XP != loadcode.Decode(XPmax) then
return false
elseif medal2 != loadcode.Decode(medalmax) then
return false
elseif medal1 != loadcode.Decode(medalmax) then
return false
endif
call loadcode.destroy()
return true
endfunction
endlibrary
//===========================================================================
function InitTrig_save_system takes nothing returns nothing
endfunction
library BigNum
//prefer algebraic approach because of real subtraction issues
function log takes real y, real base returns real
local real x
local real factor = 1.0
local real logy = 0.0
local real sign = 1.0
if(y < 0.) then
return 0.0
endif
if(y < 1.) then
set y = 1.0/y
set sign = -1.0
endif
//Chop out powers of the base
loop
exitwhen y < 1.0001 //decrease this ( bounded below by 1) to improve precision
if(y > base) then
set y = y / base
set logy = logy + factor
else
set base = SquareRoot(base) //If you use just one base a lot, precompute its squareroots
set factor = factor / 2.
endif
endloop
return sign*logy
endfunction
struct BigNum_l
integer leaf
BigNum_l next
debug static integer nalloc = 0
static method create takes nothing returns BigNum_l
local BigNum_l bl = BigNum_l.allocate()
set bl.next = 0
set bl.leaf = 0
debug set BigNum_l.nalloc = BigNum_l.nalloc + 1
return bl
endmethod
method onDestroy takes nothing returns nothing
debug set BigNum_l.nalloc = BigNum_l.nalloc - 1
endmethod
//true: want destroy
method Clean takes nothing returns boolean
if .next == 0 and .leaf == 0 then
return true
elseif .next != 0 and .next.Clean() then
call .next.destroy()
set .next = 0
return .leaf == 0
else
return false
endif
endmethod
method DivSmall takes integer base, integer denom returns integer
local integer quotient
local integer remainder = 0
local integer num
if .next != 0 then
set remainder = .next.DivSmall(base,denom)
endif
set num = .leaf + remainder*base
set quotient = num/denom
set remainder = num - quotient*denom
set .leaf = quotient
return remainder
endmethod
endstruct
struct BigNum
BigNum_l list
integer base
static method create takes integer base returns BigNum
local BigNum b = BigNum.allocate()
set b.list = 0
set b.base = base
return b
endmethod
method onDestroy takes nothing returns nothing
local BigNum_l cur = .list
local BigNum_l next
loop
exitwhen cur == 0
set next = cur.next
call cur.destroy()
set cur = next
endloop
endmethod
method IsZero takes nothing returns boolean
local BigNum_l cur = .list
loop
exitwhen cur == 0
if cur.leaf != 0 then
return false
endif
set cur = cur.next
endloop
return true
endmethod
method Dump takes nothing returns nothing
local BigNum_l cur = .list
local string s = ""
loop
exitwhen cur == 0
set s = I2S(cur.leaf)+" "+s
set cur = cur.next
endloop
call BJDebugMsg(s)
endmethod
method Clean takes nothing returns nothing
local BigNum_l cur = .list
call cur.Clean()
endmethod
//fails if bignum is null
//BASE() + carry must be less than MAXINT()
method AddSmall takes integer carry returns nothing
local BigNum_l next
local BigNum_l cur = .list
local integer sum
if cur == 0 then
set cur = BigNum_l.create()
set .list = cur
endif
loop
exitwhen carry == 0
set sum = cur.leaf + carry
set carry = sum / .base
set sum = sum - carry*.base
set cur.leaf = sum
if cur.next == 0 then
set cur.next = BigNum_l.create()
endif
set cur = cur.next
endloop
endmethod
//x*BASE() must be less than MAXINT()
method MulSmall takes integer x returns nothing
local BigNum_l cur = .list
local integer product
local integer remainder
local integer carry = 0
loop
exitwhen cur == 0 and carry == 0
set product = x * cur.leaf + carry
set carry = product/.base
set remainder = product - carry*.base
set cur.leaf = remainder
if cur.next == 0 and carry != 0 then
set cur.next = BigNum_l.create()
endif
set cur = cur.next
endloop
endmethod
//Returns remainder
method DivSmall takes integer denom returns integer
return .list.DivSmall(.base,denom)
endmethod
method LastDigit takes nothing returns integer
local BigNum_l cur = .list
local BigNum_l next
loop
set next = cur.next
exitwhen next == 0
set cur = next
endloop
return cur.leaf
endmethod
endstruct
private function prop_Allocator1 takes nothing returns boolean
local BigNum b1
local BigNum b2
set b1 = BigNum.create(37)
call b1.destroy()
set b2 = BigNum.create(37)
call b2.destroy()
return b1 == b2
endfunction
private function prop_Allocator2 takes nothing returns boolean
local BigNum b1
local boolean b = false
set b1 = BigNum.create(37)
call b1.AddSmall(17)
call b1.MulSmall(19)
debug if BigNum_l.nalloc < 1 then
debug return false
debug endif
call b1.destroy()
debug set b = BigNum_l.nalloc == 0
return b
endfunction
private function prop_Arith takes nothing returns boolean
local BigNum b1
set b1 = BigNum.create(37)
call b1.AddSmall(73)
call b1.MulSmall(39)
call b1.AddSmall(17)
//n = 2864
if b1.DivSmall(100) != 64 then
return false
elseif b1.DivSmall(7) != 0 then
return false
elseif b1.IsZero() then
return false
elseif b1.DivSmall(3) != 1 then
return false
elseif b1.DivSmall(3) != 1 then
return false
elseif not b1.IsZero() then
return false
endif
return true
endfunction
endlibrary
//===========================================================================
function InitTrig_bignum_lib takes nothing returns nothing
endfunction
library PlayerUtils
/**************************************************************
*
* v1.2.8 by TriggerHappy
*
* This library provides a struct which caches data about players
* as well as provides functionality for manipulating player colors.
*
* Constants
* ------------------
*
* force FORCE_PLAYING - Player group of everyone who is playing.
*
* Struct API
* -------------------
* struct User
*
* static method fromIndex takes integer i returns User
* static method fromLocal takes nothing returns User
* static method fromPlaying takes integer id returns User
*
* static method operator [] takes integer id returns User
* static method operator count takes nothing returns integer
*
* method operator name takes nothing returns string
* method operator name= takes string name returns nothing
* method operator color takes nothing returns playercolor
* method operator color= takes playercolor c returns nothing
* method operator defaultColor takes nothing returns playercolor
* method operator hex takes nothing returns string
* method operator nameColored takes nothing returns string
*
* method toPlayer takes nothing returns player
* method colorUnits takes playercolor c returns nothing
*
* readonly string originalName
* readonly boolean isPlaying
* readonly static player Local
* readonly static integer LocalId
* readonly static integer AmountPlaying
* readonly static playercolor array Color
* readonly static player array PlayingPlayer
*
**************************************************************/
globals
// automatically change unit colors when changing player color
private constant boolean AUTO_COLOR_UNITS = true
// use an array for name / color lookups (instead of function calls)
private constant boolean ARRAY_LOOKUP = false
// this only applies if ARRAY_LOOKUP is true
private constant boolean HOOK_SAFETY = false // disable for speed, but only use the struct to change name/color safely
constant force FORCE_PLAYING = CreateForce()
private string array Name
private string array Hex
private string array OriginalHex
private playercolor array CurrentColor
endglobals
private keyword PlayerUtilsInit
struct User extends array
static constant integer NULL = 15
readonly player handle
readonly integer id
readonly thistype next
readonly thistype prev
readonly string originalName
readonly boolean isPlaying
readonly static thistype first
readonly static thistype last
readonly static player Local
readonly static integer LocalId
readonly static integer AmountPlaying = 0
readonly static playercolor array Color
static if not (LIBRARY_GroupUtils) then
readonly static group ENUM_GROUP = CreateGroup()
endif
private static thistype array PlayingPlayer
private static integer array PlayingPlayerIndex
// similar to Player(#)
static method fromIndex takes integer i returns thistype
return thistype(i)
endmethod
// similar to GetLocalPlayer
static method fromLocal takes nothing returns thistype
return thistype(thistype.LocalId)
endmethod
// access active players array
static method fromPlaying takes integer index returns thistype
return PlayingPlayer[index]
endmethod
static method operator [] takes player p returns thistype
return thistype(GetPlayerId(p))
endmethod
method toPlayer takes nothing returns player
return this.handle
endmethod
method operator name takes nothing returns string
static if (ARRAY_LOOKUP) then
return Name[this]
else
return GetPlayerName(this.handle)
endif
endmethod
method operator name= takes string newName returns nothing
call SetPlayerName(this.handle, newName)
static if (ARRAY_LOOKUP) then
static if not (HOOK_SAFETY) then
set Name[this] = newName
endif
endif
endmethod
method operator color takes nothing returns playercolor
static if (ARRAY_LOOKUP) then
return CurrentColor[this]
else
return GetPlayerColor(this.handle)
endif
endmethod
method operator hex takes nothing returns string
return OriginalHex[GetHandleId(this.color)]
endmethod
method operator color= takes playercolor c returns nothing
call SetPlayerColor(this.handle, c)
static if (ARRAY_LOOKUP) then
set CurrentColor[this] = c
static if not (HOOK_SAFETY) then
static if (AUTO_COLOR_UNITS) then
call this.colorUnits(color)
endif
endif
endif
endmethod
method operator defaultColor takes nothing returns playercolor
return Color[this]
endmethod
method operator nameColored takes nothing returns string
return hex + this.name + "|r"
endmethod
method colorUnits takes playercolor c returns nothing
local unit u
call GroupEnumUnitsOfPlayer(ENUM_GROUP, this.handle, null)
loop
set u = FirstOfGroup(ENUM_GROUP)
exitwhen u == null
call SetUnitColor(u, c)
call GroupRemoveUnit(ENUM_GROUP, u)
endloop
endmethod
static method onLeave takes nothing returns boolean
local thistype p = thistype[GetTriggerPlayer()]
local integer i = .PlayingPlayerIndex[p.id]
// clean up
call ForceRemovePlayer(FORCE_PLAYING, p.toPlayer())
// recycle index
set .AmountPlaying = .AmountPlaying - 1
set .PlayingPlayerIndex[i] = .PlayingPlayerIndex[.AmountPlaying]
set .PlayingPlayer[i] = .PlayingPlayer[.AmountPlaying]
if (.AmountPlaying == 1) then
set p.prev.next = User.NULL
set p.next.prev = User.NULL
else
set p.prev.next = p.next
set p.next.prev = p.prev
endif
set .last = .PlayingPlayer[.AmountPlaying]
set p.isPlaying = false
return false
endmethod
implement PlayerUtilsInit
endstruct
private module PlayerUtilsInit
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
local thistype p
set thistype.Local = GetLocalPlayer()
set thistype.LocalId = GetPlayerId(thistype.Local)
set OriginalHex[0] = "|cffff0303"
set OriginalHex[1] = "|cff0042ff"
set OriginalHex[2] = "|cff1ce6b9"
set OriginalHex[3] = "|cff540081"
set OriginalHex[4] = "|cfffffc01"
set OriginalHex[5] = "|cfffe8a0e"
set OriginalHex[6] = "|cff20c000"
set OriginalHex[7] = "|cffe55bb0"
set OriginalHex[8] = "|cff959697"
set OriginalHex[9] = "|cff7ebff1"
set OriginalHex[10] = "|cff106246"
set OriginalHex[11] = "|cff4e2a04"
set thistype.first = User.NULL
loop
exitwhen i == 12
set p = User(i)
set p.handle = Player(i)
set p.id = i
set thistype.Color[i] = GetPlayerColor(p.handle)
set CurrentColor[i] = thistype.Color[i]
if (GetPlayerController(p.handle) == MAP_CONTROL_USER and GetPlayerSlotState(p.handle) == PLAYER_SLOT_STATE_PLAYING) then
set .PlayingPlayer[AmountPlaying] = p
set .PlayingPlayerIndex[i] = .AmountPlaying
set .last = i
if (.first == User.NULL) then
set .first = i
set User(i).next = User.NULL
set User(i).prev = User.NULL
else
set User(i).prev = PlayingPlayer[AmountPlaying-1].id
set PlayingPlayer[AmountPlaying-1].next = User(i)
set User(i).next = User.NULL
endif
set p.isPlaying = true
call TriggerRegisterPlayerEvent(t, p.handle, EVENT_PLAYER_LEAVE)
call ForceAddPlayer(FORCE_PLAYING, p.handle)
set Hex[p] = OriginalHex[GetHandleId(thistype.Color[i])]
set .AmountPlaying = .AmountPlaying + 1
endif
set Name[p] = GetPlayerName(p.handle)
set p.originalName=Name[p]
set i = i + 1
endloop
call TriggerAddCondition(t, Filter(function thistype.onLeave))
endmethod
endmodule
//===========================================================================
static if (ARRAY_LOOKUP) then
static if (HOOK_SAFETY) then
private function SetPlayerNameHook takes player whichPlayer, string name returns nothing
set Name[GetPlayerId(whichPlayer)] = name
endfunction
private function SetPlayerColorHook takes player whichPlayer, playercolor color returns nothing
local User p = User[whichPlayer]
set Hex[p] = OriginalHex[GetHandleId(color)]
set CurrentColor[p] = color
static if (AUTO_COLOR_UNITS) then
call p.colorUnits(color)
endif
endfunction
hook SetPlayerName SetPlayerNameHook
hook SetPlayerColor SetPlayerColorHook
endif
endif
endlibrary
library GroupUtils initializer Init requires optional xebasic
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This library is a combination of several features relevant to groups. First
//* and foremost, it contains a group stack that you can access dynamic groups
//* from. It also provides means to refresh groups and clear any shadow
//* references within them. The included boolexprs are there for backwards
//* compatibility with maps that happen to use them. Since the 1.24c patch,
//* null boolexprs used in GroupEnumUnits* calls no longer leak, so there is no
//* performance gain to using the BOOLEXPR_TRUE constant.
//*
//* Instead of creating/destroying groups, we have moved on to recycling them.
//* NewGroup pulls a group from the stack and ReleaseGroup adds it back. Always
//* remember to call ReleaseGroup on a group when you are done using it. If you
//* fail to do so enough times, the stack will overflow and no longer work.
//*
//* GroupRefresh cleans a group of any shadow references which may be clogging
//* its hashtable. If you remove a unit from the game who is a member of a unit
//* group, it will 'effectively' remove the unit from the group, but leave a
//* shadow in its place. Calling GroupRefresh on a group will clean up any
//* shadow references that may exist within it. It is only worth doing this on
//* groups that you plan to have around for awhile.
//*
//* Constants that can be used from the library:
//* [group] ENUM_GROUP As you might expect, this group is good for
//* when you need a group just for enumeration.
//* [boolexpr] BOOLEXPR_TRUE This is a true boolexpr, which is important
//* because a 'null' boolexpr in enumeration
//* calls results in a leak. Use this instead.
//* [boolexpr] BOOLEXPR_FALSE This exists mostly for completeness.
//*
//* This library also includes a simple implementation of a group enumeration
//* call that factors collision of units in a given area of effect. This is
//* particularly useful because GroupEnumUnitsInRange doesn't factor collision.
//*
//* In your map, you can just replace all instances of GroupEnumUnitsInRange
//* with GroupEnumUnitsInArea with identical arguments and your spells will
//* consider all units colliding with the area of effect. After calling this
//* function as you would normally call GroupEnumUnitsInRange, you are free to
//* do anything with the group that you would normally do.
//*
//* If you don't use xebasic in your map, you may edit the MAX_COLLISION_SIZE
//* variable below and the library will use that as the added radius to check.
//* If you use xebasic, however, the script will automatically use xe's
//* collision size variable.
//*
//* You are also able to use GroupUnitsInArea. This function returns all units
//* within the area, no matter what they are, which can be convenient for those
//* instances where you actually want that.
//*
//* Example usage:
//* local group MyGroup = NewGroup()
//* call GroupRefresh(MyGroup)
//* call ReleaseGroup(MyGroup)
//* call GroupEnumUnitsInArea(ENUM_GROUP, x, y, 350., BOOLEXPR_TRUE)
//* call GroupUnitsInArea(ENUM_GROUP, x, y, 350.)
//*
globals
//If you don't have xebasic in your map, this value will be used instead.
//This value corresponds to the max collision size of a unit in your map.
private constant real MAX_COLLISION_SIZE = 197.
//If you are insane and don't care about any of the protection involved in
//this library, but want this script to be really fast, set this to true.
private constant boolean LESS_SAFETY = false
endglobals
globals
//* Constants that are available to the user
group ENUM_GROUP = CreateGroup()
boolexpr BOOLEXPR_TRUE = null
boolexpr BOOLEXPR_FALSE = null
endglobals
globals
//* Hashtable for debug purposes
private hashtable ht = InitHashtable()
//* Temporary references for GroupRefresh
private boolean Flag = false
private group Refr = null
//* Arrays and counter for the group stack
private group array Groups
private integer Count = 0
//* Variables for use with the GroupUnitsInArea function
private real X = 0.
private real Y = 0.
private real R = 0.
private hashtable H = InitHashtable()
endglobals
private function HookDestroyGroup takes group g returns nothing
if g == ENUM_GROUP then
call BJDebugMsg(SCOPE_PREFIX+"Warning: ENUM_GROUP destroyed")
endif
endfunction
debug hook DestroyGroup HookDestroyGroup
private function AddEx takes nothing returns nothing
if Flag then
call GroupClear(Refr)
set Flag = false
endif
call GroupAddUnit(Refr, GetEnumUnit())
endfunction
function GroupRefresh takes group g returns nothing
set Flag = true
set Refr = g
call ForGroup(Refr, function AddEx)
if Flag then
call GroupClear(g)
endif
endfunction
function NewGroup takes nothing returns group
if Count == 0 then
set Groups[0] = CreateGroup()
else
set Count = Count - 1
endif
static if not LESS_SAFETY then
call SaveInteger(ht, 0, GetHandleId(Groups[Count]), 1)
endif
return Groups[Count]
endfunction
function ReleaseGroup takes group g returns boolean
local integer id = GetHandleId(g)
static if LESS_SAFETY then
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
else
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif not HaveSavedInteger(ht, 0, id) then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Group not part of stack")
return false
elseif LoadInteger(ht, 0, id) == 2 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Groups cannot be multiply released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
call SaveInteger(ht, 0, id, 2)
endif
call GroupClear(g)
set Groups[Count] = g
set Count = Count + 1
return true
endfunction
private function Filter takes nothing returns boolean
return IsUnitInRangeXY(GetFilterUnit(), X, Y, R)
endfunction
private function HookDestroyBoolExpr takes boolexpr b returns nothing
local integer bid = GetHandleId(b)
if HaveSavedHandle(H, 0, bid) then
//Clear the saved boolexpr
call DestroyBoolExpr(LoadBooleanExprHandle(H, 0, bid))
call RemoveSavedHandle(H, 0, bid)
endif
endfunction
hook DestroyBoolExpr HookDestroyBoolExpr
private constant function GetRadius takes real radius returns real
static if LIBRARY_xebasic then
return radius+XE_MAX_COLLISION_SIZE
else
return radius+MAX_COLLISION_SIZE
endif
endfunction
function GroupEnumUnitsInArea takes group whichGroup, real x, real y, real radius, boolexpr filter returns nothing
local real prevX = X
local real prevY = Y
local real prevR = R
local integer bid = 0
//Set variables to new values
set X = x
set Y = y
set R = radius
if filter == null then
//Adjusts for null boolexprs passed to the function
set filter = Condition(function Filter)
else
//Check for a saved boolexpr
set bid = GetHandleId(filter)
if HaveSavedHandle(H, 0, bid) then
//Set the filter to use to the saved one
set filter = LoadBooleanExprHandle(H, 0, bid)
else
//Create a new And() boolexpr for this filter
set filter = And(Condition(function Filter), filter)
call SaveBooleanExprHandle(H, 0, bid, filter)
endif
endif
//Enumerate, if they want to use the boolexpr, this lets them
call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), filter)
//Give back original settings so nested enumerations work
set X = prevX
set Y = prevY
set R = prevR
endfunction
function GroupUnitsInArea takes group whichGroup, real x, real y, real radius returns nothing
local real prevX = X
local real prevY = Y
local real prevR = R
//Set variables to new values
set X = x
set Y = y
set R = radius
//Enumerate
call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), Condition(function Filter))
//Give back original settings so nested enumerations work
set X = prevX
set Y = prevY
set R = prevR
endfunction
private function True takes nothing returns boolean
return true
endfunction
private function False takes nothing returns boolean
return false
endfunction
private function Init takes nothing returns nothing
set BOOLEXPR_TRUE = Condition(function True)
set BOOLEXPR_FALSE = Condition(function False)
endfunction
endlibrary
library xebasic
//**************************************************************************
//
// xebasic 0.4
// =======
// XE_DUMMY_UNITID : Rawcode of the dummy unit in your map. It should
// use the dummy.mdx model, so remember to import it as
// well, just use copy&paste to copy the dummy from the
// xe map to yours, then change the rawcode.
//
// XE_HEIGHT_ENABLER: Medivh's raven form ability, you may need to change
// this rawcode to another spell that morphs into a flier
// in case you modified medivh's spell in your map.
//
// XE_TREE_RECOGNITION: The ancients' Eat tree ability, same as with medivh
// raven form, you might have to change it.
//
// XE_ANIMATION_PERIOD: The global period of animation used by whatever
// timer that depends on it, if you put a low value
// the movement will look good but it may hurt your
// performance, if instead you use a high value it
// will not lag but will be fast.
//
// XE_MAX_COLLISION_SIZE: The maximum unit collision size in your map, if
// you got a unit bigger than 197.0 it would be
// a good idea to update this constant, since some
// enums will not find it. Likewise, if none of
// your units can go bellow X and X is much smaller
// than 197.0, it would be a good idea to update
// as well, since it will improve the performance
// those enums.
//
// Notice you probably don't have to update this library, unless I specify
// there are new constants which would be unlikely.
//
//**************************************************************************
//===========================================================================
globals
constant integer XE_DUMMY_UNITID = 'e000'
constant integer XE_HEIGHT_ENABLER = 'Amrf'
constant integer XE_TREE_RECOGNITION = 'Aeat'
constant real XE_ANIMATION_PERIOD = 0.025
constant real XE_MAX_COLLISION_SIZE = 197.0
endglobals
endlibrary