Name | Type | is_array | initial_value |
library NeatMessages initializer Init
/*
===========================================================================================
Neat Text Messages
by Antares
Recreation of the default text messages with more customizability.
How to import:
Copy this library into your map. To get better looking messages with text shadow and an optional
tooltip-like box around the text, copy the "NeatTextMessage.fdf" and "NeatMessageTemplates.toc"
files from the test map into your map without a subpath.
Edit the parameters in the config section to your liking.
Replace all DisplayTextToForce calls etc. with the appropriate function from this library.
GUI users: You can use the REPLACE_BLIZZARD_FUNCTION_CALLS feature. This will replace all calls
automatically. You don't need to do anything else. If you want to setup multiple message formats,
copy the GUI globals into your map and look at how to setup custom formats with the examples provided
in this map. This is not necessary if you want to stick exclusively to the message format you define
in the config here.
WARNING: Calling neat message functions (but not clear functions) from within local player code
with REPLACE_BLIZZARD_FUNCTION_CALLS will cause a desync!
Default text formatting can be overwritten by setting up NeatFormats. Examples are given in the
test section. All formatting parameters that aren't set for a NeatFormat will use the default
values instead.
NeatMessage creator functions return an integer. This integer is a pointer to the created message
that can be used to edit, extend, or remove the message. The returned integer is asynchronous
and will be 0 for all players for whom the message isn't displayed.
You can set up additional text windows with NeatWindow.create. If you create a neat message
without specifying the window in which it should be created, it will always be created in the
default window specified in the config. Additional windows are not available with the GUI
features.
===========================================================================================
API
===========================================================================================
NeatMessage takes string whichMessage returns nothing
NeatMessageToPlayer takes player whichPlayer, string whichMessage returns nothing
NeatMessageToForce takes force whichForce, string whichMessage returns nothing
NeatMessageTimed takes real duration, string whichMessage returns nothing
NeatMessageToPlayerTimed takes player whichPlayer, real duration, string whichMessage returns nothing
NeatMessageToForceTimed takes force whichForce, real duration, string whichMessage returns nothing
NeatMessageFormatted takes string whichMessage, NeatFormat whichFormat returns nothing
NeatMessageToPlayerFormatted takes player whichPlayer, string whichMessage, NeatFormat whichFormat returns nothing
NeatMessageToForceFormatted takes force whichForce, string whichMessage, NeatFormat whichFormat returns nothing
NeatMessageTimedFormatted takes real duration, string whichMessage, NeatFormat whichFormat returns nothing
NeatMessageToPlayerTimedFormatted takes player whichPlayer, real duration, string whichMessage, NeatFormat whichFormat returns nothing
NeatMessageToForceTimedFormatted takes force whichForce, real duration, string whichMessage, NeatFormat whichFormat returns nothing
NeatMessageInWindow takes string whichMessage, NeatWindow whichWindow returns nothing
NeatMessageToPlayerInWindow takes player whichPlayer, string whichMessage, NeatWindow whichWindow returns nothing
NeatMessageToForceInWindow takes force whichForce, string whichMessage, NeatWindow whichWindow returns nothing
NeatMessageTimedInWindow takes real duration, string whichMessage, NeatWindow whichWindow returns nothing
NeatMessageToPlayerTimedInWindow takes player whichPlayer, real duration, string whichMessage, NeatWindow whichWindow returns nothing
NeatMessageToForceTimedInWindow takes force whichForce, real duration, string whichMessage, NeatWindow whichWindow returns nothing
NeatMessageFormattedInWindow takes string whichMessage, NeatFormat whichFormat, NeatWindow whichWindow returns nothing
NeatMessageToPlayerFormattedInWindow takes player whichPlayer, string whichMessage, NeatFormat whichFormat, NeatWindow whichWindow returns nothing
NeatMessageToForceFormattedInWindow takes force whichForce, string whichMessage, NeatFormat whichFormat, NeatWindow whichWindow returns nothing
NeatMessageTimedFormattedInWindow takes real duration, string whichMessage, NeatFormat whichFormat, NeatWindow whichWindow returns nothing
NeatMessageToPlayerTimedFormattedInWindow takes player whichPlayer, real duration, string whichMessage, NeatFormat whichFormat, NeatWindow whichWindow returns nothing
NeatMessageToForceTimedFormattedInWindow takes force whichForce, real duration, string whichMessage, NeatFormat whichFormat, NeatWindow whichWindow returns nothing
===========================================================================================
EditNeatMessage takes integer messagePointer, string newText returns nothing
AddNeatMessageTimeRemaining takes integer messagePointer, real additionalTime returns nothing
SetNeatMessageTimeRemaining takes integer messagePointer, real newTime returns nothing
RemoveNeatMessage takes integer messagePointer returns nothing
AutoSetNeatMessageTimeRemaining takes integer messagePointer, boolean accountForTimeElapsed returns nothing
IsMessageDisplayed takes integer messagePointer returns boolean
NeatMessageAddIcon takes integer messagePointer, real width, real height, string orientation, string texture
(valid arguments for orientation are "topleft", "topright", "bottomleft", "bottomright")
NeatMessageHideIcon takes integer messagePointer returns nothing
ClearNeatMessages takes nothing returns nothing
ClearNeatMessagesForPlayer takes player whichPlayer returns nothing
ClearNeatMessagesForForce takes force whichForce returns nothing
ClearNeatMessagesInWindow takes NeatWindow whichWindow returns nothing
ClearNeatMessagesForPlayerInWindow takes player whichPlayer, NeatWindow whichWindow returns nothing
ClearNeatMessagesForForceInWindow takes force whichForce, NeatWindow whichWindow returns nothing
set myFormat = NeatFormat.create takes nothing returns NeatFormat
set myFormat.spacing =
set myFormat.fadeOutTime =
set myFormat.fadeInTime =
set myFormat.fontSize =
set myFormat.minDuration =
set myFormat.durationIncrease =
set myFormat.verticalAlignment =
set myFormat.horizontalAlignment =
set myFormat.isBoxed =
call myFormat.copy(copiedFormat)
set myWindow = NeatWindow.create takes real xPosition, real yPosition, real width, real height, integer maxMessages, boolean topToBottom returns NeatWindow
===========================================================================================
GUI API
===========================================================================================
CreateNeatFormat takes string formatName returns nothing
SetNeatFormat takes string formatName returns nothing
ResetNeatVars takes nothing returns nothing
ResetNeatFormat takes nothing returns nothing
===========================================================================================
*/
globals
//=========================================================================================
//Config
//=========================================================================================
//Default text formatting. Can be overwritten by setting up neatFormats.
private constant real MESSAGE_MINIMUM_DURATION = 2.5 //Display duration of a message with zero characters.
private constant real MESSAGE_DURATION_INCREASE_PER_CHARACTER = 0.12
private constant real TEXT_MESSAGE_FONT_SIZE = 14
private constant real SPACING_BETWEEN_MESSAGES = 0.0
private constant real FADE_IN_TIME = 0.0
private constant real FADE_OUT_TIME = 1.8
private constant textaligntype VERTICAL_ALIGNMENT = TEXT_JUSTIFY_MIDDLE //TEXT_JUSTIFY_BOTTOM, TEXT_JUSTIFY_MIDDLE, or TEXT_JUSTIFY_TOP
private constant textaligntype HORIZONTAL_ALIGNMENT = TEXT_JUSTIFY_CENTER //TEXT_JUSTIFY_LEFT, TEXT_JUSTIFY_CENTER, or TEXT_JUSTIFY_RIGHT
private constant boolean BOXED_MESSAGES = false //Create tooltip box around text messages? Requires .fdf file and INCLUDE_FDF enabled.
//Default text window parameters.
constant real TEXT_MESSAGE_X_POSITION = 0.225 //0 = left, 1 = right (bottom-left corner)
constant real TEXT_MESSAGE_Y_POSITION = 0.16 //0 = bottom, 0.6 = top (bottom-left corner)
constant real TEXT_MESSAGE_BLOCK_MAX_HEIGHT = 0.2 //Maximum height of the entire text message block. Messages pushed out of that area will be removed.
constant real TEXT_MESSAGE_BLOCK_WIDTH = 0.35
constant integer MAX_TEXT_MESSAGES = 5 //Maximum number of messages on the screen at the same time. If you want a non-scrolling window, simply set this number to 1.
constant boolean MESSAGE_ORDER_TOP_TO_BOTTOM = false //Set true if new messages should appear above old messages.
//Config
private constant boolean INCLUDE_FDF = true //NeatMessage.fdf has been imported?
private constant boolean COPY_TO_MESSAGE_LOG = true //(Only singleplayer) Copies messages to message log by printing out the message with DisplayTextToPlayer, then clearing all text. Will interfere with other default text messages.
private constant boolean REPLACE_BLIZZARD_FUNCTION_CALLS = false //Replaces Display(Timed)TextToForce, ClearTextMessages, and ClearTextMessagesBJ.
private constant integer TOOLTIP_ABILITY = 'Amls' //For REPLACE_BLIZZARD_FUNCTION_CALLS only. Any unused ability for which the library can change the tooltip to extract the TRIGSTR from.
//=========================================================================================
private boolean isSinglePlayer
private timer masterTimer = CreateTimer()
private timer clearTextTimer = CreateTimer()
private integer numMessagesOnScreen = 0
private boolean doNotClear = false
private integer messageCounter = 0
private integer array frameOfMessage
private NeatWindow array windowOfMessage
private constant real TIME_STEP = 0.05
NeatFormat DEFAULT_NEAT_FORMAT
NeatWindow DEFAULT_NEAT_WINDOW
private NeatWindow array neatWindow
private integer numNeatWindows = 0
endglobals
static if REPLACE_BLIZZARD_FUNCTION_CALLS then
hook DisplayTextToForce DisplayTextToForceHook
hook DisplayTimedTextToForce DisplayTimedTextToForceHook
hook ClearTextMessagesBJ ClearNeatMessagesForForce
hook ClearTextMessages ClearNeatMessages
endif
//=========================================================================================
private function ClearText takes nothing returns nothing
set doNotClear = true
call ClearTextMessages()
set doNotClear = false
endfunction
private function GetAdjustedStringLength takes string whichString returns integer
local integer rawLength = StringLength(whichString)
local integer adjustedLength = rawLength
local integer j = 0
local string secondCharacter
loop
exitwhen j > rawLength - 10
if SubString(whichString, j, j+1) == "|" then
set secondCharacter = StringCase(SubString(whichString, j+1, j+2), false)
if secondCharacter == "c" then
set adjustedLength = adjustedLength - 10
set j = j + 10
elseif secondCharacter == "r" then
set adjustedLength = adjustedLength - 2
set j = j + 2
endif
else
set j = j + 1
endif
endloop
return adjustedLength
endfunction
private function ChangeTextFormatting takes NeatWindow w, integer whichFrame, NeatFormat whichFormat returns nothing
if whichFormat == 0 then
return
endif
set w.messageFormat[whichFrame] = whichFormat
call BlzFrameSetScale(w.textCarryingFrame[whichFrame], whichFormat.fontSize/10.)
call BlzFrameSetTextAlignment( w.textCarryingFrame[whichFrame], whichFormat.verticalAlignment, whichFormat.horizontalAlignment )
endfunction
globals
NeatWindow myWindow
endglobals
private function ChangeText takes NeatWindow w, integer whichFrame, string whichText returns nothing
local real height
call BlzFrameSetText( w.textCarryingFrame[whichFrame] , whichText )
if w.maxTextMessages == 1 then
call BlzFrameSetSize( w.textCarryingFrame[whichFrame] , w.width / (w.messageFormat[whichFrame].fontSize/10.) , w.maxHeight / (w.messageFormat[whichFrame].fontSize/10.) )
else
call BlzFrameSetSize( w.textCarryingFrame[whichFrame] , w.width / (w.messageFormat[whichFrame].fontSize/10.) , 0 )
call BlzFrameSetSize( w.textCarryingFrame[whichFrame] , w.width / (w.messageFormat[whichFrame].fontSize/10.) , 0 )
endif
static if INCLUDE_FDF then
call BlzFrameSetSize( w.textMessageFrame[whichFrame] , BlzFrameGetWidth(w.textMessageText[whichFrame]) + 0.008 , BlzFrameGetHeight(w.textMessageText[whichFrame]) + 0.009 )
call BlzFrameSetPoint( w.textMessageBox[whichFrame] , FRAMEPOINT_BOTTOMLEFT , w.textMessageFrame[whichFrame] , FRAMEPOINT_BOTTOMLEFT , 0 , -0.0007*(w.messageFormat[whichFrame].fontSize-13) )
call BlzFrameSetPoint( w.textMessageBox[whichFrame] , FRAMEPOINT_TOPRIGHT , w.textMessageFrame[whichFrame] , FRAMEPOINT_TOPRIGHT , 0 , 0 )
set myWindow = w
endif
if w.textMessageIconOrientation[whichFrame] != null then
set height = BlzFrameGetHeight(w.textMessageIcon[whichFrame])
if BlzFrameGetHeight( w.textMessageFrame[whichFrame] ) < height then
call BlzFrameSetSize( w.textCarryingFrame[whichFrame] , w.width / (w.messageFormat[whichFrame].fontSize/10.) , height - 0.018 )
static if INCLUDE_FDF then
call BlzFrameSetSize( w.textMessageFrame[whichFrame] , BlzFrameGetWidth(w.textMessageText[whichFrame]) + 0.008 , BlzFrameGetHeight(w.textMessageText[whichFrame]) + 0.009 )
call BlzFrameSetPoint( w.textMessageBox[whichFrame] , FRAMEPOINT_BOTTOMLEFT , w.textMessageFrame[whichFrame] , FRAMEPOINT_BOTTOMLEFT , 0 , -0.0007*(w.messageFormat[whichFrame].fontSize-13) )
call BlzFrameSetPoint( w.textMessageBox[whichFrame] , FRAMEPOINT_TOPRIGHT , w.textMessageFrame[whichFrame] , FRAMEPOINT_TOPRIGHT , 0 , 0 )
endif
set w.textHeight[whichFrame] = BlzFrameGetHeight(w.textMessageFrame[whichFrame]) + RMaxBJ(w.messageFormat[whichFrame].spacing , w.messageFormat[whichFrame+1].spacing)
endif
endif
if whichText == "" then
call BlzFrameSetVisible( w.textMessageFrame[whichFrame] , false )
call BlzFrameSetAlpha( w.textMessageFrame[whichFrame] , 255 )
else
call BlzFrameSetVisible( w.textMessageFrame[whichFrame] , true )
endif
endfunction
private function HideTextMessage takes NeatWindow w, integer whichFrame, boolean collapseFrame returns nothing
if BlzFrameGetText(w.textCarryingFrame[whichFrame]) != "" then
set numMessagesOnScreen = numMessagesOnScreen - 1
endif
call ChangeText(w,whichFrame,"")
set w.messageTimeRemaining[whichFrame] = 0
set frameOfMessage[w.messageOfFrame[whichFrame]] = -1
set w.messageOfFrame[whichFrame] = 0
if collapseFrame then
set w.textHeight[whichFrame] = 0
endif
if w.textMessageIconOrientation[whichFrame] != null then
call BlzFrameSetVisible( w.textMessageIcon[whichFrame] , false )
call BlzFrameSetAlpha( w.textMessageIcon[whichFrame] , 255 )
endif
endfunction
private function FadeoutLoop takes nothing returns nothing
local integer i
local integer j = 1
local real scale
local NeatWindow w
if numMessagesOnScreen == 0 then
return
endif
loop
exitwhen j > numNeatWindows
set w = neatWindow[j]
set i = 0
loop
exitwhen i == w.maxTextMessages
if w.messageTimeRemaining[i] > 0 then
set w.messageTimeRemaining[i] = w.messageTimeRemaining[i] - TIME_STEP
set w.messageTimeElapsed[i] = w.messageTimeElapsed[i] + TIME_STEP
if w.messageTimeRemaining[i] < w.messageFormat[i].fadeOutTime then
if w.messageTimeRemaining[i] < 0 then
call HideTextMessage(w,i,false)
else
call BlzFrameSetAlpha( w.textMessageFrame[i] , R2I(255*w.messageTimeRemaining[i]/w.messageFormat[i].fadeOutTime) )
endif
elseif w.messageTimeElapsed[i] < w.messageFormat[i].fadeInTime then
call BlzFrameSetAlpha( w.textMessageFrame[i] , R2I(255*w.messageTimeElapsed[i]/w.messageFormat[i].fadeInTime) )
endif
if w.textMessageIconOrientation[i] != null then
call BlzFrameSetAlpha( w.textMessageIcon[i] , BlzFrameGetAlpha(w.textMessageFrame[i]) )
endif
endif
set i = i + 1
endloop
set j = j + 1
endloop
endfunction
private function RepositionAllMessages takes NeatWindow w returns nothing
local integer i
local real array yOffset
local real textBlockHeight
//=========================================================================================
//Get message heights
//=========================================================================================
set yOffset[0] = 0
set textBlockHeight = w.textHeight[0]
set i = 1
loop
exitwhen i > w.maxTextMessages - 1
set yOffset[i] = yOffset[i-1] + w.textHeight[i-1]
if BlzFrameGetText(w.textCarryingFrame[i]) != "" then
set textBlockHeight = RMinBJ( yOffset[i] + w.textHeight[i] , w.maxHeight )
endif
set i = i + 1
endloop
//=========================================================================================
//Reposition messages
//=========================================================================================
set i = 0
loop
exitwhen i > w.maxTextMessages - 1
if yOffset[i] + w.textHeight[i] > w.maxHeight then
call HideTextMessage(w,i,true)
elseif w.isTopToBottom then
call BlzFrameSetAbsPoint( w.textMessageFrame[i] , FRAMEPOINT_BOTTOMLEFT , w.xPosition , w.yPosition - w.textHeight[i] - yOffset[i] )
else
call BlzFrameSetAbsPoint( w.textMessageFrame[i] , FRAMEPOINT_BOTTOMLEFT , w.xPosition , w.yPosition + yOffset[i] )
endif
set i = i + 1
endloop
endfunction
private function AddTextMessage takes string whichText, real forcedDuration, NeatFormat whichFormat, NeatWindow w returns integer
local integer i
local real array yOffset
local real textBlockHeight
local framehandle tempFrame
if whichText == "" then
return 0
endif
static if COPY_TO_MESSAGE_LOG and not REPLACE_BLIZZARD_FUNCTION_CALLS then
if isSinglePlayer then
call DisplayTextToPlayer( GetLocalPlayer() , 0 , 0 , whichText + "
" )
call ClearTextMessages()
endif
endif
if BlzFrameGetText(w.textCarryingFrame[w.maxTextMessages - 1]) == "" then
set numMessagesOnScreen = numMessagesOnScreen + 1
endif
//=========================================================================================
//Transfer messages to next frame
//=========================================================================================
set tempFrame = w.textMessageIcon[w.maxTextMessages - 1]
if w.textMessageIconOrientation[w.maxTextMessages - 1] != null then
call BlzFrameSetVisible( w.textMessageIcon[w.maxTextMessages - 1] , false )
call BlzFrameSetAlpha( w.textMessageIcon[w.maxTextMessages - 1] , 255 )
endif
set i = w.maxTextMessages - 2
loop
exitwhen i < 0
set w.messageTimeRemaining[i+1] = w.messageTimeRemaining[i]
set w.messageTimeElapsed[i+1] = w.messageTimeElapsed[i]
set w.textMessageIcon[i+1] = w.textMessageIcon[i]
set w.textMessageIconOrientation[i+1] = w.textMessageIconOrientation[i]
if w.textMessageIconOrientation[i] != null then
if w.textMessageIconOrientation[i+1] == "topleft" then
call BlzFrameSetPoint( w.textMessageIcon[i+1] , FRAMEPOINT_TOPRIGHT , w.textMessageFrame[i+1] , FRAMEPOINT_TOPLEFT , 0 , 0 )
elseif w.textMessageIconOrientation[i+1] == "topright" then
call BlzFrameSetPoint( w.textMessageIcon[i+1] , FRAMEPOINT_TOPLEFT , w.textMessageFrame[i+1] , FRAMEPOINT_TOPRIGHT , 0 , 0 )
elseif w.textMessageIconOrientation[i+1] == "bottomleft" then
call BlzFrameSetPoint( w.textMessageIcon[i+1] , FRAMEPOINT_BOTTOMRIGHT , w.textMessageFrame[i+1] , FRAMEPOINT_BOTTOMLEFT , 0 , 0 )
elseif w.textMessageIconOrientation[i+1] == "bottomright" then
call BlzFrameSetPoint( w.textMessageIcon[i+1] , FRAMEPOINT_BOTTOMLEFT , w.textMessageFrame[i+1] , FRAMEPOINT_BOTTOMRIGHT , 0 , 0 )
endif
endif
call ChangeTextFormatting(w, i+1, w.messageFormat[i])
set w.textHeight[i+1] = w.textHeight[i]
call ChangeText(w, i+1, BlzFrameGetText(w.textCarryingFrame[i]))
if w.messageOfFrame[i] != 0 then
set w.messageOfFrame[i+1] = w.messageOfFrame[i]
set frameOfMessage[w.messageOfFrame[i+1]] = i + 1
endif
if w.messageTimeRemaining[i+1] < w.messageFormat[i+1].fadeOutTime then
call BlzFrameSetAlpha( w.textMessageFrame[i+1] , R2I(255*w.messageTimeRemaining[i+1]/w.messageFormat[i+1].fadeOutTime) )
else
call BlzFrameSetAlpha( w.textMessageFrame[i+1] , 255 )
endif
call BlzFrameSetVisible( w.textMessageFrame[i+1] , true )
static if INCLUDE_FDF then
call BlzFrameSetVisible( w.textMessageBox[i+1] , BlzFrameIsVisible(w.textMessageBox[i]) )
endif
set i = i - 1
endloop
set w.textMessageIcon[0] = tempFrame
//=========================================================================================
//Setup new message
//=========================================================================================
call ChangeTextFormatting(w, 0, whichFormat)
call ChangeText(w, 0, whichText)
set w.textHeight[0] = BlzFrameGetHeight(w.textMessageFrame[0]) + RMaxBJ(whichFormat.spacing , w.messageFormat[1].spacing)
static if INCLUDE_FDF then
call BlzFrameSetVisible( w.textMessageBox[0], whichFormat.isBoxed )
endif
if forcedDuration != 0 then
set w.messageTimeRemaining[0] = forcedDuration + whichFormat.fadeOutTime
else
set w.messageTimeRemaining[0] = whichFormat.minDuration + whichFormat.durationIncrease*GetAdjustedStringLength(whichText) + whichFormat.fadeOutTime
endif
set w.messageTimeElapsed[0] = 0
if whichFormat.fadeInTime > 0 then
call BlzFrameSetAlpha(w.textMessageFrame[0] , 0)
else
call BlzFrameSetAlpha(w.textMessageFrame[0] , 255)
endif
call BlzFrameSetVisible( w.textMessageFrame[0] , true )
set w.textMessageIconOrientation[0] = null
if messageCounter == JASS_MAX_ARRAY_SIZE then
set messageCounter = 1
else
set messageCounter = messageCounter + 1
endif
set w.messageOfFrame[0] = messageCounter
set frameOfMessage[messageCounter] = 0
set windowOfMessage[messageCounter] = w
if w.maxTextMessages > 1 then
call RepositionAllMessages(w)
endif
return messageCounter
endfunction
private function Init takes nothing returns nothing
local integer i = 0
local integer p
local integer numPlayers
static if INCLUDE_FDF then
call BlzLoadTOCFile("NeatMessageTemplates.toc")
endif
call TimerStart( masterTimer , TIME_STEP , true , function FadeoutLoop )
set DEFAULT_NEAT_FORMAT = NeatFormat.create()
set DEFAULT_NEAT_WINDOW = NeatWindow.create(TEXT_MESSAGE_X_POSITION, TEXT_MESSAGE_Y_POSITION, TEXT_MESSAGE_BLOCK_WIDTH, TEXT_MESSAGE_BLOCK_MAX_HEIGHT, MAX_TEXT_MESSAGES, MESSAGE_ORDER_TOP_TO_BOTTOM)
static if COPY_TO_MESSAGE_LOG then
set p = 0
set numPlayers = 0
loop
exitwhen p > 23
if GetPlayerSlotState(Player(p)) == PLAYER_SLOT_STATE_PLAYING and GetPlayerController(Player(p)) == MAP_CONTROL_USER then
set numPlayers = numPlayers + 1
endif
set p = p + 1
endloop
set isSinglePlayer = numPlayers == 1
endif
static if REPLACE_BLIZZARD_FUNCTION_CALLS then
set currentNeatFormat = NeatFormat.create()
call ExecuteFunc("ResetNeatVars")
endif
endfunction
//===========================================================================================
struct NeatFormat
real spacing = SPACING_BETWEEN_MESSAGES
real fadeOutTime = FADE_OUT_TIME
real fadeInTime = FADE_IN_TIME
real fontSize = TEXT_MESSAGE_FONT_SIZE
real minDuration = MESSAGE_MINIMUM_DURATION
real durationIncrease = MESSAGE_DURATION_INCREASE_PER_CHARACTER
textaligntype verticalAlignment = VERTICAL_ALIGNMENT
textaligntype horizontalAlignment = HORIZONTAL_ALIGNMENT
boolean isBoxed = BOXED_MESSAGES
static method create takes nothing returns NeatFormat
return NeatFormat.allocate()
endmethod
method copy takes NeatFormat copiedFormat returns nothing
set .spacing = copiedFormat.spacing
set .fadeOutTime = copiedFormat.fadeOutTime
set .fadeInTime = copiedFormat.fadeInTime
set .fontSize = copiedFormat.fontSize
set .minDuration = copiedFormat.minDuration
set .durationIncrease = copiedFormat.durationIncrease
set .verticalAlignment = copiedFormat.verticalAlignment
set .horizontalAlignment = copiedFormat.horizontalAlignment
set .isBoxed = copiedFormat.isBoxed
endmethod
endstruct
struct NeatWindow
real xPosition
real yPosition
real maxHeight
real width
integer maxTextMessages
boolean isTopToBottom
framehandle array textMessageFrame[MAX_TEXT_MESSAGES]
framehandle array textMessageText[MAX_TEXT_MESSAGES]
framehandle array textMessageBox[MAX_TEXT_MESSAGES]
framehandle array textCarryingFrame[MAX_TEXT_MESSAGES]
framehandle array textMessageIcon[MAX_TEXT_MESSAGES]
NeatFormat array messageFormat[MAX_TEXT_MESSAGES]
real array messageTimeRemaining[MAX_TEXT_MESSAGES]
real array messageTimeElapsed[MAX_TEXT_MESSAGES]
real array textHeight[MAX_TEXT_MESSAGES]
string array textMessageIconOrientation[MAX_TEXT_MESSAGES]
integer array messageOfFrame[MAX_TEXT_MESSAGES]
integer numMessagesOnScreen = 0
static method create takes real xPosition, real yPosition, real width, real maxHeight, integer maxTextMessages, boolean topToBottom returns NeatWindow
local NeatWindow this = NeatWindow.allocate()
local integer i = 0
set .xPosition = xPosition
set .yPosition = yPosition
set .maxHeight = maxHeight
set .width = width
set .maxTextMessages = maxTextMessages
set .isTopToBottom = topToBottom
if isTopToBottom then
set .yPosition = .yPosition + maxHeight
endif
set numNeatWindows = numNeatWindows + 1
set neatWindow[numNeatWindows] = this
loop
exitwhen i > maxTextMessages - 1
static if INCLUDE_FDF then
set .textMessageFrame[i] = BlzCreateFrame("TextMessage", BlzGetOriginFrame(ORIGIN_FRAME_WORLD_FRAME, 0), 0, 0)
set .textMessageText[i] = BlzFrameGetChild( .textMessageFrame[i],0)
set .textMessageBox[i] = BlzFrameGetChild( .textMessageFrame[i],1)
call BlzFrameSetSize(.textMessageText[i], width/(TEXT_MESSAGE_FONT_SIZE/10.), 0)
call BlzFrameSetScale(.textMessageText[i], TEXT_MESSAGE_FONT_SIZE/10.)
call BlzFrameSetAbsPoint(.textMessageFrame[i], FRAMEPOINT_BOTTOMLEFT, xPosition, yPosition)
call BlzFrameSetTextAlignment(.textMessageText[i] , VERTICAL_ALIGNMENT , HORIZONTAL_ALIGNMENT)
call BlzFrameSetVisible( .textMessageFrame[i] , false )
call BlzFrameSetEnable(.textMessageFrame[i],false)
call BlzFrameSetEnable(.textMessageText[i],false)
call BlzFrameSetLevel(.textMessageText[i],1)
call BlzFrameSetLevel(.textMessageBox[i],0)
set .textCarryingFrame[i] = .textMessageText[i]
set .textMessageIcon[i] = BlzCreateFrameByType("BACKDROP", "textMessageIcon" + I2S(i) , BlzGetFrameByName("ConsoleUIBackdrop",0), "", 0)
call BlzFrameSetEnable(.textMessageIcon[i],false)
call BlzFrameSetVisible(.textMessageIcon[i],false)
set .messageFormat[i] = DEFAULT_NEAT_FORMAT
call ChangeTextFormatting(this, i, DEFAULT_NEAT_FORMAT)
call ChangeText(this, i, "")
else
set .textMessageFrame[i] = BlzCreateFrame("TextMessage", BlzGetOriginFrame(ORIGIN_FRAME_WORLD_FRAME, 0), 0, 0)
call BlzFrameSetScale(.textMessageFrame[i], TEXT_MESSAGE_FONT_SIZE/10.)
call BlzFrameSetTextAlignment(.textMessageFrame[i] , VERTICAL_ALIGNMENT , HORIZONTAL_ALIGNMENT)
call BlzFrameSetAbsPoint(.textMessageFrame[i], FRAMEPOINT_BOTTOMLEFT, xPosition, yPosition)
call BlzFrameSetVisible( .textMessageFrame[i] , false )
call BlzFrameSetEnable(.textMessageFrame[i],false)
set .textCarryingFrame[i] = .textMessageFrame[i]
set .textMessageIcon[i] = BlzCreateFrameByType("BACKDROP", "textMessageIcon" + I2S(i) , BlzGetFrameByName("ConsoleUIBackdrop",0), "", 0)
call BlzFrameSetEnable(.textMessageIcon[i],false)
call BlzFrameSetVisible(.textMessageIcon[i],false)
set .messageFormat[i] = DEFAULT_NEAT_FORMAT
call ChangeTextFormatting(this, i, DEFAULT_NEAT_FORMAT)
call ChangeText(this, i, "")
endif
set i = i + 1
endloop
return this
endmethod
endstruct
//===========================================================================================
//API
//===========================================================================================
//Constructors
//===========================================================================================
function NeatMessage takes string message returns integer
return AddTextMessage(message, 0, DEFAULT_NEAT_FORMAT, DEFAULT_NEAT_WINDOW)
endfunction
function NeatMessageToPlayer takes player whichPlayer, string message returns integer
if GetLocalPlayer() == whichPlayer then
return AddTextMessage(message , 0, DEFAULT_NEAT_FORMAT, DEFAULT_NEAT_WINDOW)
endif
return 0
endfunction
function NeatMessageToForce takes force whichForce, string message returns integer
if IsPlayerInForce( GetLocalPlayer() , whichForce ) then
return AddTextMessage(message, 0, DEFAULT_NEAT_FORMAT, DEFAULT_NEAT_WINDOW)
endif
return 0
endfunction
function NeatMessageTimed takes real duration, string message returns integer
return AddTextMessage(message, duration, DEFAULT_NEAT_FORMAT, DEFAULT_NEAT_WINDOW)
endfunction
function NeatMessageToPlayerTimed takes player whichPlayer, real duration, string message returns integer
if GetLocalPlayer() == whichPlayer then
return AddTextMessage(message, duration, DEFAULT_NEAT_FORMAT, DEFAULT_NEAT_WINDOW)
endif
return 0
endfunction
function NeatMessageToForceTimed takes force whichForce, real duration, string message returns integer
if IsPlayerInForce( GetLocalPlayer() , whichForce ) then
return AddTextMessage(message, duration, DEFAULT_NEAT_FORMAT, DEFAULT_NEAT_WINDOW)
endif
return 0
endfunction
function NeatMessageFormatted takes string message, NeatFormat whichFormat returns integer
return AddTextMessage(message, 0, whichFormat, DEFAULT_NEAT_WINDOW)
endfunction
function NeatMessageToPlayerFormatted takes player whichPlayer, string message, NeatFormat whichFormat returns integer
if GetLocalPlayer() == whichPlayer then
return AddTextMessage(message , 0, whichFormat, DEFAULT_NEAT_WINDOW)
endif
return 0
endfunction
function NeatMessageToForceFormatted takes force whichForce, string message, NeatFormat whichFormat returns integer
if IsPlayerInForce( GetLocalPlayer() , whichForce ) then
return AddTextMessage(message, 0, whichFormat, DEFAULT_NEAT_WINDOW)
endif
return 0
endfunction
function NeatMessageToPlayerTimedFormatted takes player whichPlayer, real duration, string message, NeatFormat whichFormat returns integer
if GetLocalPlayer() == whichPlayer then
return AddTextMessage(message, duration, whichFormat, DEFAULT_NEAT_WINDOW)
endif
return 0
endfunction
function NeatMessageToForceTimedFormatted takes force whichForce, real duration, string message, NeatFormat whichFormat returns integer
if IsPlayerInForce( GetLocalPlayer() , whichForce ) then
return AddTextMessage(message, duration, whichFormat, DEFAULT_NEAT_WINDOW)
endif
return 0
endfunction
function NeatMessageTimedFormatted takes real duration, string message, NeatFormat whichFormat returns integer
return AddTextMessage(message, duration, whichFormat, DEFAULT_NEAT_WINDOW)
endfunction
function NeatMessageInWindow takes string message, NeatWindow whichWindow returns integer
return AddTextMessage(message, 0, DEFAULT_NEAT_FORMAT, whichWindow)
endfunction
function NeatMessageToPlayerInWindow takes player whichPlayer, string message, NeatWindow whichWindow returns integer
if GetLocalPlayer() == whichPlayer then
return AddTextMessage(message , 0, DEFAULT_NEAT_FORMAT, whichWindow)
endif
return 0
endfunction
function NeatMessageToForceInWindow takes force whichForce, string message, NeatWindow whichWindow returns integer
if IsPlayerInForce( GetLocalPlayer() , whichForce ) then
return AddTextMessage(message, 0, DEFAULT_NEAT_FORMAT, whichWindow)
endif
return 0
endfunction
function NeatMessageTimedInWindow takes real duration, string message, NeatWindow whichWindow returns integer
return AddTextMessage(message, duration, DEFAULT_NEAT_FORMAT, whichWindow)
endfunction
function NeatMessageToPlayerTimedInWindow takes player whichPlayer, real duration, string message, NeatWindow whichWindow returns integer
if GetLocalPlayer() == whichPlayer then
return AddTextMessage(message, duration, DEFAULT_NEAT_FORMAT, whichWindow)
endif
return 0
endfunction
function NeatMessageToForceTimedInWindow takes force whichForce, real duration, string message, NeatWindow whichWindow returns integer
if IsPlayerInForce( GetLocalPlayer() , whichForce ) then
return AddTextMessage(message, duration, DEFAULT_NEAT_FORMAT, whichWindow)
endif
return 0
endfunction
function NeatMessageFormattedInWindow takes string message, NeatFormat whichFormat, NeatWindow whichWindow returns integer
return AddTextMessage(message, 0, whichFormat, whichWindow)
endfunction
function NeatMessageToPlayerFormattedInWindow takes player whichPlayer, string message, NeatFormat whichFormat, NeatWindow whichWindow returns integer
if GetLocalPlayer() == whichPlayer then
return AddTextMessage(message , 0, whichFormat, whichWindow)
endif
return 0
endfunction
function NeatMessageToForceFormattedInWindow takes force whichForce, string message, NeatFormat whichFormat, NeatWindow whichWindow returns integer
if IsPlayerInForce( GetLocalPlayer() , whichForce ) then
return AddTextMessage(message, 0, whichFormat, whichWindow)
endif
return 0
endfunction
function NeatMessageToPlayerTimedFormattedInWindow takes player whichPlayer, real duration, string message, NeatFormat whichFormat, NeatWindow whichWindow returns integer
if GetLocalPlayer() == whichPlayer then
return AddTextMessage(message, duration, whichFormat, whichWindow)
endif
return 0
endfunction
function NeatMessageToForceTimedFormattedInWindow takes force whichForce, real duration, string message, NeatFormat whichFormat, NeatWindow whichWindow returns integer
if IsPlayerInForce( GetLocalPlayer() , whichForce ) then
return AddTextMessage(message, duration, whichFormat, whichWindow)
endif
return 0
endfunction
function NeatMessageTimedFormattedInWindow takes real duration, string message, NeatFormat whichFormat, NeatWindow whichWindow returns integer
return AddTextMessage(message, duration, whichFormat, whichWindow)
endfunction
//Utility
//===========================================================================================
function EditNeatMessage takes integer messagePointer, string newText returns nothing
local integer whichFrame = frameOfMessage[messagePointer]
local NeatWindow whichWindow = windowOfMessage[messagePointer]
if messagePointer == 0 or whichFrame == -1 then
return
endif
call ChangeText(whichWindow, whichFrame, newText)
set whichWindow.textHeight[whichFrame] = BlzFrameGetHeight(whichWindow.textMessageFrame[whichFrame]) + RMaxBJ(whichWindow.messageFormat[whichFrame].spacing , whichWindow.messageFormat[whichFrame+1].spacing)
call RepositionAllMessages(whichWindow)
endfunction
function AddNeatMessageTimeRemaining takes integer messagePointer, real additionalTime returns nothing
local integer whichFrame = frameOfMessage[messagePointer]
local NeatWindow whichWindow = windowOfMessage[messagePointer]
if messagePointer == 0 or whichFrame == -1 then
return
endif
set whichWindow.messageTimeRemaining[whichFrame] = RMaxBJ(whichWindow.messageTimeRemaining[whichFrame] + additionalTime, whichWindow.messageFormat[whichFrame].fadeOutTime)
endfunction
function SetNeatMessageTimeRemaining takes integer messagePointer, real newTime returns nothing
local integer whichFrame = frameOfMessage[messagePointer]
local NeatWindow whichWindow = windowOfMessage[messagePointer]
if messagePointer == 0 or whichFrame == -1 then
return
endif
set whichWindow.messageTimeRemaining[whichFrame] = RMaxBJ(newTime, whichWindow.messageFormat[whichFrame].fadeOutTime)
endfunction
function AutoSetNeatMessageTimeRemaining takes integer messagePointer, boolean accountForTimeElapsed returns nothing
local integer whichFrame = frameOfMessage[messagePointer]
local NeatWindow whichWindow = windowOfMessage[messagePointer]
local NeatFormat whichFormat = whichWindow.messageFormat[whichFrame]
if messagePointer == 0 or whichFrame == -1 then
return
endif
set whichWindow.messageTimeRemaining[whichFrame] = whichFormat.minDuration + whichFormat.durationIncrease*GetAdjustedStringLength(BlzFrameGetText(whichWindow.textCarryingFrame[whichFrame])) + whichFormat.fadeOutTime
if accountForTimeElapsed then
set whichWindow.messageTimeRemaining[whichFrame] = RMaxBJ(whichWindow.messageTimeRemaining[whichFrame] - whichWindow.messageTimeElapsed[whichFrame], whichFormat.fadeOutTime)
endif
endfunction
function RemoveNeatMessage takes integer messagePointer returns nothing
local integer whichFrame = frameOfMessage[messagePointer]
local NeatWindow whichWindow = windowOfMessage[messagePointer]
if messagePointer == 0 or whichFrame == -1 then
return
endif
set whichWindow.messageTimeRemaining[whichFrame] = whichWindow.messageFormat[whichFrame].fadeOutTime
endfunction
function IsNeatMessageDisplayed takes integer messagePointer returns boolean
return frameOfMessage[messagePointer] != -1
endfunction
function NeatMessageAddIcon takes integer messagePointer, real width, real height, string orientation, string texture returns nothing
local integer whichFrame = frameOfMessage[messagePointer]
local NeatWindow whichWindow = windowOfMessage[messagePointer]
call BlzFrameSetVisible( whichWindow.textMessageIcon[whichFrame] , true )
call BlzFrameSetAlpha( whichWindow.textMessageIcon[whichFrame] , 255 )
call BlzFrameSetSize( whichWindow.textMessageIcon[whichFrame] , width , height )
if orientation == "topleft" then
call BlzFrameSetPoint( whichWindow.textMessageIcon[whichFrame] , FRAMEPOINT_TOPRIGHT , whichWindow.textMessageFrame[whichFrame] , FRAMEPOINT_TOPLEFT , 0 , 0 )
elseif orientation == "topright" then
call BlzFrameSetPoint( whichWindow.textMessageIcon[whichFrame] , FRAMEPOINT_TOPLEFT , whichWindow.textMessageFrame[whichFrame] , FRAMEPOINT_TOPRIGHT , 0 , 0 )
elseif orientation == "bottomleft" then
call BlzFrameSetPoint( whichWindow.textMessageIcon[whichFrame] , FRAMEPOINT_BOTTOMRIGHT , whichWindow.textMessageFrame[whichFrame] , FRAMEPOINT_BOTTOMLEFT , 0 , 0 )
elseif orientation == "bottomright" then
call BlzFrameSetPoint( whichWindow.textMessageIcon[whichFrame] , FRAMEPOINT_BOTTOMLEFT , whichWindow.textMessageFrame[whichFrame] , FRAMEPOINT_BOTTOMRIGHT , 0 , 0 )
else
debug call BJDebugMsg("Invalid icon orientation...")
return
endif
call BlzFrameSetTexture( whichWindow.textMessageIcon[whichFrame] , texture , 0 , true )
set whichWindow.textMessageIconOrientation[whichFrame] = orientation
if BlzFrameGetHeight( whichWindow.textMessageFrame[whichFrame] ) < height then
call BlzFrameSetSize( whichWindow.textCarryingFrame[whichFrame] , whichWindow.width / (whichWindow.messageFormat[whichFrame].fontSize/10.) , height - 0.018 )
static if INCLUDE_FDF then
call BlzFrameSetSize( whichWindow.textMessageFrame[whichFrame] , BlzFrameGetWidth(whichWindow.textMessageText[whichFrame]) + 0.008 , BlzFrameGetHeight(whichWindow.textMessageText[whichFrame]) + 0.009 )
call BlzFrameSetPoint( whichWindow.textMessageBox[whichFrame] , FRAMEPOINT_BOTTOMLEFT , whichWindow.textMessageFrame[whichFrame] , FRAMEPOINT_BOTTOMLEFT , 0 , -0.0007*(whichWindow.messageFormat[whichFrame].fontSize-13) )
call BlzFrameSetPoint( whichWindow.textMessageBox[whichFrame] , FRAMEPOINT_TOPRIGHT , whichWindow.textMessageFrame[whichFrame] , FRAMEPOINT_TOPRIGHT , 0 , 0 )
endif
set whichWindow.textHeight[whichFrame] = BlzFrameGetHeight(whichWindow.textMessageFrame[whichFrame]) + RMaxBJ(whichWindow.messageFormat[whichFrame].spacing , whichWindow.messageFormat[whichFrame+1].spacing)
call RepositionAllMessages(whichWindow)
endif
endfunction
function NeatMessageHideIcon takes integer messagePointer returns nothing
local integer whichFrame = frameOfMessage[messagePointer]
local NeatWindow whichWindow = windowOfMessage[messagePointer]
call BlzFrameSetVisible( whichWindow.textMessageIcon[whichFrame] , false )
set whichWindow.textMessageIconOrientation[whichFrame] = null
endfunction
function ClearNeatMessagesForPlayer takes player whichPlayer returns nothing
local integer i
local integer j = 1
if GetLocalPlayer() == whichPlayer then
loop
exitwhen j > numNeatWindows
set i = 0
loop
exitwhen i > neatWindow[j].maxTextMessages - 1
call HideTextMessage(neatWindow[j], i, true)
set i = i + 1
endloop
set j = j + 1
endloop
endif
endfunction
function ClearNeatMessages takes nothing returns nothing
local integer i
local integer j = 1
if doNotClear then
return
endif
loop
exitwhen j > numNeatWindows
set i = 0
loop
exitwhen i > neatWindow[j].maxTextMessages - 1
call HideTextMessage(neatWindow[j], i, true)
set i = i + 1
endloop
set j = j + 1
endloop
endfunction
function ClearNeatMessagesForForce takes force whichForce returns nothing
local integer i
local integer j = 1
if IsPlayerInForce(GetLocalPlayer() , whichForce) then
loop
exitwhen j > numNeatWindows
set i = 0
loop
exitwhen i > neatWindow[j].maxTextMessages - 1
call HideTextMessage(neatWindow[j], i, true)
set i = i + 1
endloop
set j = j + 1
endloop
endif
endfunction
function ClearNeatMessagesForPlayerInWindow takes player whichPlayer, NeatWindow whichWindow returns nothing
local integer i
if GetLocalPlayer() == whichPlayer then
set i = 0
loop
exitwhen i > whichWindow.maxTextMessages - 1
call HideTextMessage(whichWindow, i, true)
set i = i + 1
endloop
endif
endfunction
function ClearNeatMessagesInWindow takes NeatWindow whichWindow returns nothing
local integer i
set i = 0
loop
exitwhen i > whichWindow.maxTextMessages - 1
call HideTextMessage(whichWindow, i, true)
set i = i + 1
endloop
endfunction
function ClearNeatMessagesForForceInWindow takes force whichForce, NeatWindow whichWindow returns nothing
local integer i
if IsPlayerInForce(GetLocalPlayer() , whichForce) then
set i = 0
loop
exitwhen i > whichWindow.maxTextMessages - 1
call HideTextMessage(whichWindow, i, true)
set i = i + 1
endloop
endif
endfunction
//===========================================================================================
//GUI API
//===========================================================================================
static if REPLACE_BLIZZARD_FUNCTION_CALLS then
globals
NeatFormat currentNeatFormat
private hashtable GUIhash = InitHashtable()
endglobals
function CreateNeatFormat takes string formatName returns nothing
local NeatFormat whichFormat = NeatFormat.create()
call SaveInteger( GUIhash , StringHash(formatName) , 0 , whichFormat )
set whichFormat.spacing = udg_spacing
set whichFormat.minDuration = udg_minDuration
set whichFormat.durationIncrease = udg_durationIncrease
set whichFormat.isBoxed = udg_isBoxed
set whichFormat.fadeOutTime = udg_fadeOutTime
set whichFormat.fadeInTime = udg_fadeInTime
set whichFormat.fontSize = udg_fontSize
if udg_verticalAlignment == "bottom" then
set whichFormat.verticalAlignment = TEXT_JUSTIFY_BOTTOM
elseif udg_verticalAlignment == "middle" then
set whichFormat.verticalAlignment = TEXT_JUSTIFY_MIDDLE
elseif udg_verticalAlignment == "top" then
set whichFormat.verticalAlignment = TEXT_JUSTIFY_TOP
else
debug call BJDebugMsg("Invalid vertical alignment specificed. Valid types are bottom, middle, and top.")
endif
if udg_horizontalAlignment == "left" then
set whichFormat.horizontalAlignment = TEXT_JUSTIFY_LEFT
elseif udg_horizontalAlignment == "center" then
set whichFormat.horizontalAlignment = TEXT_JUSTIFY_CENTER
elseif udg_horizontalAlignment == "right" then
set whichFormat.horizontalAlignment = TEXT_JUSTIFY_RIGHT
else
debug call BJDebugMsg("Invalid horizontal alignment specificed. Valid types are left, center, and right.")
endif
endfunction
function SetNeatFormat takes string formatName returns nothing
local NeatFormat whichFormat = LoadInteger( GUIhash , StringHash(formatName) , 0 )
if whichFormat == 0 then
debug call BJDebugMsg("Could not find neat format with specified name...")
else
set currentNeatFormat = whichFormat
endif
endfunction
function ResetNeatVars takes nothing returns nothing
set udg_spacing = SPACING_BETWEEN_MESSAGES
set udg_minDuration = MESSAGE_MINIMUM_DURATION
set udg_durationIncrease = MESSAGE_DURATION_INCREASE_PER_CHARACTER
set udg_isBoxed = BOXED_MESSAGES
set udg_fadeOutTime = FADE_OUT_TIME
set udg_fadeInTime = FADE_IN_TIME
set udg_fontSize = TEXT_MESSAGE_FONT_SIZE
if VERTICAL_ALIGNMENT == TEXT_JUSTIFY_BOTTOM then
set udg_verticalAlignment = "bottom"
elseif VERTICAL_ALIGNMENT == TEXT_JUSTIFY_MIDDLE then
set udg_verticalAlignment = "middle"
elseif VERTICAL_ALIGNMENT == TEXT_JUSTIFY_TOP then
set udg_verticalAlignment = "top"
endif
if HORIZONTAL_ALIGNMENT == TEXT_JUSTIFY_LEFT then
set udg_horizontalAlignment = "left"
elseif HORIZONTAL_ALIGNMENT == TEXT_JUSTIFY_CENTER then
set udg_horizontalAlignment = "center"
elseif HORIZONTAL_ALIGNMENT == TEXT_JUSTIFY_RIGHT then
set udg_horizontalAlignment = "right"
endif
endfunction
function ResetNeatFormat takes nothing returns nothing
set currentNeatFormat = DEFAULT_NEAT_FORMAT
endfunction
//===========================================================================================
//GUI Helper functions
//===========================================================================================
function DisplayTextToForceHook takes force whichForce, string message returns nothing
local string extractedString
call BlzSetAbilityTooltip( TOOLTIP_ABILITY , message , 0 )
set extractedString = BlzGetAbilityTooltip( TOOLTIP_ABILITY , 0 )
call NeatMessageToForceFormatted( whichForce , extractedString , currentNeatFormat )
call TimerStart( clearTextTimer , 0.0 , false , function ClearText )
endfunction
function DisplayTimedTextToForceHook takes force whichForce, real duration, string message returns nothing
local string extractedString
call BlzSetAbilityTooltip( TOOLTIP_ABILITY , message , 0 )
set extractedString = BlzGetAbilityTooltip( TOOLTIP_ABILITY , 0 )
call NeatMessageToForceTimedFormatted( whichForce , duration , extractedString, currentNeatFormat )
call TimerStart( clearTextTimer , 0.0 , false , function ClearText )
endfunction
endif
endlibrary
/*
==========================================================================================================================================================
Antares' Hero Selection
==========================================================================================================================================================
A P I
==========================================================================================================================================================
function InitHeroSelection takes nothing returns nothing //Creates hero list, player list, and frames.
function BeginHeroSelection takes nothing returns nothing //Enforces camera and/or shows menu, based on config.
function EnableHeroSelection takes nothing returns nothing //Allows heroes to be picked.
function EnablePlayerHeroSelection takes player whichPlayer, boolean enable returns nothing
function EnablePlayerHeroPreselection takes player whichPlayer, boolean enable returns nothing
function PlayerEscapeHeroSelection takes player whichPlayer returns nothing //Hides frames and unlocks camera for player.
function PlayerReturnToHeroSelection takes player whichPlayer returns nothing //Allows player to repick his or her hero.
function HeroSelectionAllRandom takes nothing returns nothing
function HeroSelectionPlayerForceSelect takes player whichPlayer, Hero whichHero returns nothing
function HeroSelectionPlayerForceRandom takes player whichPlayer returns nothing
function HeroSelectionForceEnd takes nothing returns nothing //Will give each player who hasn't picked a random hero.
function BanHeroFromSelection takes Hero whichHero, boolean disable returns nothing //Hero will remain in the menu.
function BanHeroFromSelectionForPlayer takes Hero whichHero, player whichPlayer, boolean disable returns nothing
function HeroSelectionSetTimeRemaining takes real time returns nothing
function HeroSelectionAddTimeRemaining takes real time returns nothing
function RestartHeroSelection takes nothing returns nothing //For game restarts. Resets all variables and calls BeginHeroSelection. No one can be in hero selection at the time.
function StartHeroBanPhase takes real timeLimit returns nothing //Starts a ban phase with specified time limit, after which EnableHeroSelection is called. Requires MENU_INCLUDE_BAN_BUTTON.
function StartHeroSelectionTimer takes real timeLimit, code callback returns nothing //Starts the timer that is displayed during hero selection with a custom callback function.
function NoOneInHeroSelection takes nothing returns boolean
function EveryoneHasHero takes nothing returns boolean
==========================================================================================================================================================
How to import:
Copy into empty triggers in your map:
HeroSelectionDocumentation
HeroSelectionConfig
HeroSelectionCallbacks
HeroDeclaration
HeroSelection
Import:
HeroSelectionMenu.fdf
CustomTooltip.fdf
HeroSelectionTemplates.toc
GeneralHeroGlow.mdx (optional)
Copy Destructables:
Hero No Shadow (optional)
Hero Shadow (optional)
===========================================================================================
Getting started:
HeroSelection has a long config that you should go through from top to bottom, with the
customization options becoming more and more specific as you progress. The fine-tuning
section has a vast number of options, which may look overwhelming, but you do not need
to edit any of them to get a working system.
To get a working system, here is the To-Do List:
1. Set up the terrain and the hero selection camera.
2. Import your heroes into HeroDeclaration and set the fields.
3. Set up tooltips for your heroes.
4. Create a trigger in your map that initializes the hero selection.
5. Set up the interface functions OnEscape and OnFinal.
===========================================================================================
1. Hero Selection Camera
To set up the hero selection camera, edit
FOREGROUND_HERO_X
FOREGROUND_HERO_Y
HERO_SELECTION_ANGLE
===========================================================================================
2. Importing Heroes
To import your heroes, go into HeroDeclaration and create a repository for your heroes:
globals
Hero MyHero1
Hero MyHero2
...
endglobals
In the HeroDeclaration function, initialize the heroes with
set MyHero1 = Hero.create()
set MyHero2 = Hero.create()
...
Then, set the fields for your heroes as specified. Some of the variables may already be
set elsewhere in your map. In that case, I recommend writing a function to automatically
copy the variables into those fields.
Note that fields that should be "false", "null", or "0" do not have to be set as they are the
default values.
===========================================================================================
3. Tooltips
To set up a tooltips, you can either write it directly during hero declaration using the
tooltip field. You can also fetch the tooltip from the unit itself or from an ability by
setting the tooltipAbility field. To fetch it from an ability, create a new non-hero ability
with one level. Set the tooltipAbility field to the id of whichever object you want to use.
===========================================================================================
4. Initializing Hero Selection
This system has no init function that runs automatically, since it requires the hero and
player lists to initialize and you may want to set them at a later point. To initialize, do:
call InitHeroSelection()
This will create frames, create the hero list, and initialize players. To start hero selection, do:
call BeginHeroSelection()
This will enforce the hero selection camera and/or show the menu to players, depending on
your config. However, players will not be able to select heroes until you do:
call EnableHeroSelection()
===========================================================================================
5. Creating the Interface
Now that we have a trigger to begin hero selection, we have to be able to exit it and
create the heroes that were selected. To do this, edit the OnEscape and OnFinal function
in the callbacks section. The variables you can use in those functions are "heroIdOfPlayer",
which stores the unit id of the hero selected, and "heroIconPathOfPlayer", which can be useful
for updating a multiboard icon, for example.
===========================================================================================
After the hero selection system is successfully integrated into your map, go to the config
section and adjust the parameters controlling the system's behavior. Then, when everything
is working as intended, you can go into the fine-tuning section and go nuts customizing
every part of the system.
You may want to change some parameters based on the game state. In that case, make the
parameter public and un-constant it. Note, however, that changing some constants after
hero selection has been initialized might have no effect and/or lead to bugs.
===========================================================================================
You can customize the appearance of the hero selection menu background by editing the
HeroSelectionMenu.fdf. By default, the background uses the escape menu textures of whatever
race the player is playing, but this can be changed by editing BackdropBackground and
BackdropEdgeFile. Remove DecorateFileNames when using imported assets!
One important parameter is BackdropCornerSize, which controls the thickness of the border
texture. Make sure to update MENU_BORDER_TILE_SIZE accordingly, so that the textures remain
seamless.
Read more here on how to edit an .fdf file:
https://www.hiveworkshop.com/threads/ui-reading-a-fdf.315850/
===========================================================================================
Neat Messages:
The showcase map includes NeatMessages, a library designed to give more control over text
messages. With default text messages, you will most likely run into the problem that they
cannot be placed where you want them to and they might hide a UI element or the heroes as a
result. Therefore, if you want to add text messages to the hero selection, I strongly
recommend using NeatMessages.
https://www.hiveworkshop.com/threads/neat-text-messages.350336/
===========================================================================================
How to setup multiple pages:
Some maps might have more heroes than fit on one page. To setup multiple pages, divide your
heroes into multiple categories in HeroDeclaration. To hide the category captions, set the
CATEGORY_NAMES to null in the config under SetCategories. Now, set the PAGE_OF_CATEGORY
to the page at which the heroes of that category should be appear.
===========================================================================================
How to setup a draft:
To setup a draft, call BeginHeroSelection, but then, instead of using EnableHeroSelection,
call EnablePlayerHeroSelection for the player that should pick the first hero. Set a time
limit for that player picking his or her hero with StartHeroSelectionTimer(timeLimit, yourCallback).
In your callback, call HeroSelectionPlayerForceRandom. Finally, in the OnPick custom code function,
write logic to enable hero selection for the next player.
===========================================================================================
Screen coordinates:
All UI element positions are given in screen coordinates. x-coordinates go from 0 (left) to
0.8, excluding the widescreen area (no frame can be put there). y-coordinates go from 0 (bottom)
to 0.6 (top). On a 1080p monitor, 0.00056 corresponds to 1 pixel. 0.01 corresponds to ~18 pixel.
You can place the hero selection menu as well as the mouse-over tooltips into the widescreen
area by setting the x-position to a negative number / a number greater than 0.8. The width of
the widescreen area is 0.6*(16/9 - 4/3)/2 = 0.1333, so the lowest possible x-coordinate is
-0.1333 and the highest 0.9333. The position will automatically be adjusted for players who
are not using a widescreen monitor.
Read more on screen coordinates here:
https://www.hiveworkshop.com/threads/ui-positionate-frames.315860/
===========================================================================================
Page cycle button styles (for PAGE_CYCLE_BUTTON_TEXT_STYLE):
"EnvelopRandomButton" Icon-style buttons directly left and right of the random buttons.
"LeftRightMenuButton" Icon-style buttons left and right of the menu in the same row as the random buttons.
"BelowRandomButton" Icon-style buttons below the random buttons.
"LeftRightMiddleButton" Icon-style buttons in the middle of the left and right edges of the menu.
"LeftVerticalButton" Icon-style buttons in the middle of the left edge of the menu, arranged vertically.
"RightVerticalButton" Icon-style buttons in the middle of the right edge of the menu, arranged vertically.
"EnvelopAcceptButton" Text-style buttons directly left and right of the accept button.
"LeftRightBottomButton" Text-style buttons on the bottom edge in the left and right corners.
"TangentTopButton" Text-style buttons in the center of the top edge right next to each other.
"LeftRightTopButton" Text-style buttons on the top edge in the left and right corners.
*/
library HeroDeclaration
globals
Hero MountainKing
Hero Paladin
Hero Archmage
Hero BloodMage
Hero PriestessOfTheMoon
Hero DemonHunter
Hero KeeperOfTheGrove
Hero Warden
endglobals
function HeroDeclaration takes nothing returns nothing
//========================================================================================
//Here you declare all heroes in your map. The order in which you create them determine the order in which they appear in the hero selection menu.
//The fields that should be set are:
/*
integer array abilities The abilities of this hero. Starts at index 1.
boolean array isNonHeroAbility Set this flag if the ability at the index is a non-hero ability. This will make the tooltip instead of the research tooltip appear on mouse-over.
integer unitId Unit id of the hero.
integer tooltipAbility Set up any non-hero ability with one level for each hero and write the hero's description in its tooltip.
string selectEmote Sound path of the emote the hero should play when selected.
animtype selectAnim animType of select animation. For example, "spell" is ANIMTYPE_SPELL.
subanimtype selectSubAnim subanimtype of select animation. For example "spell slam" is ANIMTYPE_SPELL + SUBANIMTYPE_SLAM.
real selectAnimLength Length of the select animation. If set incorrectly, animation will be interrupted or freeze.
integer category In which category in the menu should this hero be put?
boolean needsHeroGlow A hero glow will be added to heroes with models that don't have a glow. Requires "GeneralHeroGlow.mdx" to be imported.
boolean unavailable Heroes with this flag will appear in the menu but cannot be picked.
boolean array unavailableToTeam Hero will not appear in the menu for players in a team for which this flag was set. Can be used to create completely different hero rosters for different teams.
*/
//========================================================================================
set MountainKing = Hero.create()
set Paladin = Hero.create()
set Archmage = Hero.create()
set BloodMage = Hero.create()
set PriestessOfTheMoon = Hero.create()
set DemonHunter = Hero.create()
set KeeperOfTheGrove = Hero.create()
set Warden = Hero.create()
//========================================================================================
set Archmage.abilities[1] = 'AHbz'
set Archmage.abilities[2] = 'AHwe'
set Archmage.abilities[3] = 'AHab'
set Archmage.abilities[4] = 'AHmt'
set Archmage.selectEmote = "Units\\Human\\HeroArchMage\\HeroArchMageWarcry1.flac"
set Archmage.unitId = 'Hamg'
set Archmage.tooltipAbility = 'Tamg'
set Archmage.needsHeroGlow = false
set Archmage.category = 3
set Archmage.selectAnim = ANIM_TYPE_SPELL
set Archmage.selectSubAnim = null
set Archmage.selectAnimLength = 2.7
//========================================================================================
set Paladin.abilities[1] = 'AHhb'
set Paladin.abilities[2] = 'AHds'
set Paladin.abilities[3] = 'AHad'
set Paladin.abilities[4] = 'AHre'
set Paladin.selectEmote = "Units\\Human\\HeroPaladin\\HeroPaladinYesAttack1.flac"
set Paladin.unitId = 'Hpal'
set Paladin.tooltipAbility = 'Tpal'
set Paladin.needsHeroGlow = false
set Paladin.category = 1
set Paladin.selectAnim = ANIM_TYPE_SPELL
set Paladin.selectSubAnim = null
set Paladin.selectAnimLength = 2.167
//========================================================================================
set MountainKing.abilities[1] = 'AHtb'
set MountainKing.abilities[2] = 'AHtc'
set MountainKing.abilities[3] = 'AHbh'
set MountainKing.abilities[4] = 'AHav'
set MountainKing.selectEmote = "Units\\Human\\HeroMountainKing\\HeroMountainKingYesAttack1.flac"
set MountainKing.unitId = 'Hmkg'
set MountainKing.tooltipAbility = 'Tmkg'
set MountainKing.needsHeroGlow = false
set MountainKing.category = 1
set MountainKing.selectAnim = ANIM_TYPE_SPELL
set MountainKing.selectSubAnim = SUBANIM_TYPE_SLAM
set MountainKing.selectAnimLength = 1.0
//========================================================================================
set BloodMage.abilities[1] = 'AHfs'
set BloodMage.abilities[2] = 'AHbn'
set BloodMage.abilities[3] = 'AHdr'
set BloodMage.abilities[4] = 'AHpx'
set BloodMage.selectEmote = "Units\\Human\\HeroBloodElf\\BloodElfMageYesAttack2.flac"
set BloodMage.unitId = 'Hblm'
set BloodMage.tooltipAbility = 'Tblm'
set BloodMage.needsHeroGlow = false
set BloodMage.category = 3
set BloodMage.selectAnim = ANIM_TYPE_SPELL
set BloodMage.selectSubAnim = SUBANIM_TYPE_CHANNEL
set BloodMage.selectAnimLength = 2.0
//========================================================================================
set PriestessOfTheMoon.abilities[1] = 'AEst'
set PriestessOfTheMoon.abilities[2] = 'AHfa'
set PriestessOfTheMoon.abilities[3] = 'AEar'
set PriestessOfTheMoon.abilities[4] = 'AEsf'
set PriestessOfTheMoon.selectEmote = "Units\\nightElf\\HeroMoonPriestess\\HeroMoonPriestessYesAttack2.flac"
set PriestessOfTheMoon.unitId = 'Emoo'
set PriestessOfTheMoon.tooltipAbility = 'Tmoo'
set PriestessOfTheMoon.needsHeroGlow = false
set PriestessOfTheMoon.category = 2
set PriestessOfTheMoon.selectAnim = ANIM_TYPE_SPELL
set PriestessOfTheMoon.selectSubAnim = null
set PriestessOfTheMoon.selectAnimLength = 1.333
//========================================================================================
set DemonHunter.abilities[1] = 'AEmb'
set DemonHunter.abilities[2] = 'AEim'
set DemonHunter.abilities[3] = 'AEev'
set DemonHunter.abilities[4] = 'AEme'
set DemonHunter.selectEmote = "Units\\NightElf\\HeroDemonHunter\\HeroDemonHunterYesAttack1.flac"
set DemonHunter.unitId = 'Edem'
set DemonHunter.tooltipAbility = 'Tdem'
set DemonHunter.needsHeroGlow = false
set DemonHunter.category = 2
set DemonHunter.selectAnim = ANIM_TYPE_SPELL
set DemonHunter.selectSubAnim = SUBANIM_TYPE_THROW
set DemonHunter.selectAnimLength = 0.9
//========================================================================================
set KeeperOfTheGrove.abilities[1] = 'AEer'
set KeeperOfTheGrove.abilities[2] = 'AEfn'
set KeeperOfTheGrove.abilities[3] = 'AEah'
set KeeperOfTheGrove.abilities[4] = 'AEtq'
set KeeperOfTheGrove.selectEmote = "Units\\NightElf\\HeroKeeperOfTheGrove\\KeeperOfTheGroveYesAttack2.flac"
set KeeperOfTheGrove.unitId = 'Ekee'
set KeeperOfTheGrove.tooltipAbility = 'Tkee'
set KeeperOfTheGrove.needsHeroGlow = false
set KeeperOfTheGrove.category = 3
set KeeperOfTheGrove.selectAnim = ANIM_TYPE_STAND
set KeeperOfTheGrove.selectSubAnim = SUBANIM_TYPE_VICTORY
set KeeperOfTheGrove.selectAnimLength = 3.333
//========================================================================================
set Warden.abilities[1] = 'AEfk'
set Warden.abilities[2] = 'AEbl'
set Warden.abilities[3] = 'AEsh'
set Warden.abilities[4] = 'AEsv'
set Warden.selectEmote = "Units\\nightElf\\HeroWarden\\HeroWardenYesAttack3.flac"
set Warden.unitId = 'Ewar'
set Warden.tooltipAbility = 'Twar'
set Warden.needsHeroGlow = false
set Warden.category = 2
set Warden.selectAnim = ANIM_TYPE_SPELL
set Warden.selectSubAnim = null
set Warden.selectAnimLength = 1.2
//========================================================================================
endfunction
endlibrary
library HeroSelectionCallbacks requires HeroSelection
globals
//=============================================================================================================================
//Globals for interfacing with rest of map. All player indices start at 0.
//=============================================================================================================================
integer array heroIdOfPlayer //Stores the unit id of the hero each player picked.
string array heroIconPathOfPlayer //Stores the icon path of the hero each player picked.
//=============================================================================================================================
//Can also be used in custom code. Read-Only!
boolean array playerHasHero //Player has picked a hero (does not mean hero was created).
boolean array isInHeroSelection //Player is currently viewing the hero selection screen.
boolean inBanPhase = false
boolean array playerIsHuman
string array coloredPlayerName
boolean array heroSelectionDisabledForPlayer
boolean array heroPreselectionDisabledForPlayer
boolean array playerHasBan
boolean array isRepicking
integer numPlayersWithHero = 0
integer numPlayersInSelection = 0
integer numSelectingPlayers = 0
integer numSelectingHumans = 0
integer array playerNumberOfSlot //To get the player index when iterating over all selecting players from 1 to numSelectingPlayers.
effect array selectedHero
effect array backgroundHeroHighlight
effect array backgroundHero
real array BACKGROUND_HERO_X
real array BACKGROUND_HERO_Y
integer NUMBER_OF_HEROES = 0
endglobals
//=================================================================================================================================
//These functions are the main interface with the rest of your map.
//=================================================================================================================================
function HeroSelectionOnEscape takes player whichPlayer returns nothing
//Here you can insert code that is executed when a player is kicked out of hero selection. This should include creating
//the hero that player selected as well as setting that player's camera location. Will be executed before OnFinal.
//Example code:
static if LIBRARY_TestHeroSelection then
set playerHero[GetPlayerId(whichPlayer)] = CreateUnit(whichPlayer, heroIdOfPlayer[GetPlayerId(whichPlayer)], HERO_SPAWN_X, HERO_SPAWN_Y, 90)
if GetLocalPlayer() == whichPlayer then
call SetCameraPosition(HERO_SPAWN_X, HERO_SPAWN_Y)
endif
static if LIBRARY_NeatMessages then
call ClearNeatMessages()
else
call ClearTextMessages()
endif
endif
endfunction
function HeroSelectionOnFinal takes nothing returns nothing
//Here you can insert the code that is executed when hero selection is concluded. This will most likely involve calling the main
//function that progresses your map after hero selection ends. It is recommended to execute player-specific actions in OnEscape
//instead.
//Example code:
static if LIBRARY_TestHeroSelection then
call ExecuteFunc("BeginGame")
endif
endfunction
//=================================================================================================================================
//These functions allow you to add additional visual effects, sounds, texts etc. to the hero selection that can't be achieved with
//the default options.
//=================================================================================================================================
function HeroSelectionOnRestart takes nothing returns nothing
//Here you can insert additional code that is executed when hero selection is restarted.
//Example code:
static if LIBRARY_TestHeroSelection then
local integer i = 1
local integer P
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
call DestroyEffect(circles[P])
set i = i + 1
endloop
endif
endfunction
function HeroSelectionOnLast takes nothing returns nothing
//Here you can insert additional code that is executed when the last player picks a hero.
//Example code:
static if LIBRARY_TestHeroSelection then
call ExecuteFunc("MusicSlowFadeOut")
endif
endfunction
function HeroSelectionOnPick takes player whichPlayer, Hero whichHero, boolean wasRandomPick, boolean wasRepick returns nothing
//Here you can insert additional code that is executed when a player picks a hero.
endfunction
function HeroSelectionOnPreselect takes player whichPlayer, Hero oldHero, Hero newHero returns nothing
//Here you can insert additional code that is executed when a player switches to a new hero during pre-selection.
endfunction
function HeroSelectionOnReturn takes player whichPlayer returns nothing
//Here you can insert additional code that is executed when a player returns to hero selection.
endfunction
function HeroSelectionOnAllRandom takes nothing returns nothing
//Here you can insert additional code that is executed when all random mode is selected.
endfunction
function HeroSelectionOnBegin takes nothing returns nothing
//Here you can insert additional code that is executed when players are locked into hero selection.
//Example code:
static if LIBRARY_NeatMessages and LIBRARY_TestHeroSelection then
call NeatMessageTimedInWindow(5, "Heroes from all across Azeroth have gathered to fight the darkness. Only you can save this land...", centerWindow)
endif
endfunction
function HeroSelectionOnEnable takes nothing returns nothing
//Here you can insert additional code that is executed when hero selection is enabled.
//Example code:
local integer i = 1
local integer P
local location tempLoc
static if LIBRARY_TestHeroSelection then
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
set circles[P] = AddSpecialEffect("buildings\\other\\CircleOfPower\\CircleOfPower.mdl", BACKGROUND_HERO_X[P], BACKGROUND_HERO_Y[P])
call BlzSetSpecialEffectColorByPlayer(circles[P], Player(PLAYER_NEUTRAL_AGGRESSIVE))
set tempLoc = Location(BACKGROUND_HERO_X[P], BACKGROUND_HERO_Y[P])
call BlzSetSpecialEffectZ(circles[P], GetLocationZ(tempLoc) + 10)
call RemoveLocation(tempLoc)
set i = i + 1
endloop
endif
call ClearMapMusic()
call PlayMusic("Nightsong.mp3")
endfunction
function HeroSelectionOnExpire takes nothing returns nothing
//Here you can insert the code that is executed when the timer for the hero selection duration expires.
//Example code:
static if LIBRARY_NeatMessages and LIBRARY_TestHeroSelection then
call NeatMessageTimedInWindow(4, "The time has expired.", centerWindow)
endif
endfunction
function HeroSelectionOnLeave takes player whichPlayer returns nothing
//Here you can insert additional code that is executed when a player who is in hero selection leaves the game.
endfunction
endlibrary
library HeroSelectionConfig requires optional NeatMessages
//=================================================================================================================================
//These constants determine important aspects of the hero selection. You can fine-tune visuals, sounds etc. further down below.
//=================================================================================================================================
globals
constant boolean HERO_SELECTION_ENABLE_DEBUG_MODE = true //Printout errors and check for function crashes.
//Overall behavior.
constant boolean HERO_CAN_BE_PICKED_MULTIPLE_TIMES = false //Set to true if the same hero should be able to be picked multiple times.
constant boolean AUTO_SET_SELECTING_PLAYERS = false //Set to true if hero selection should be enabled for all human players. Set to false if you want to set the player list manually (which can include computer players).
constant boolean COMPUTER_AUTO_PICK_RANDOM_HERO = false //Set to true if computer players that are in hero selection should automatically pick a random hero after all human players have picked.
constant boolean ESCAPE_PLAYER_AFTER_SELECTING = false //Set to true if a player picking a hero should kick him or her out of the hero selection menu and camera.
constant boolean CONCEAL_HERO_PICKS_FROM_ENEMIES = false //Set to true if hero picks should be concealed from enemies (players in another team or all players if there are no teams), including emote, effect, and text messages.
constant boolean MENU_INCLUDE_RANDOM_PICK = true //Include button for picking a random hero at the bottom of the menu.
constant boolean MENU_INCLUDE_SUGGEST_RANDOM = true //Include button to pre-select a random hero next to the random pick button.
constant real TIME_LIMIT = 60 //Set a time limit after which players who haven't chosen a hero will be assigned one at random. Set to 0 for no time limit.
//Camera and foreground hero positions (background hero locations are set in GetPlayerBackgroundHeroLocation).
constant boolean SEPARATE_LOCATIONS_FOR_EACH_TEAM = false //Set to true if each team gets a different selection screen (all following values will be ignored and you have to set the array equivalents in InitArrays).
constant real FOREGROUND_HERO_X = -128 //x-Position of foreground hero.
constant real FOREGROUND_HERO_Y = 256 //y-Position of foreground hero.
constant real HERO_SELECTION_ANGLE = 0 //Hero selection viewing direction of camera (0 = facing east, 90 = facing north etc.).
//Visuals.
constant boolean ENFORCE_CAMERA = true //Set to false if players' camera should not be changed during hero selection.
constant boolean HIDE_GAME_UI = true //Set to true if all UI elements other than the hero selection menu should be hidden.
constant boolean CREATE_FOREGROUND_HERO = true //Set to false if no foreground hero should appear on preselection.
constant boolean CREATE_BACKGROUND_HEROES = true //Set to true if players should be able to see other players' picks in the background.
endglobals
//=================================================================================================================================
globals
string array CATEGORY_NAMES //Names of the hero categories that appear in the hero selection menu.
integer array PAGE_OF_CATEGORY //Set the page at which that category appears within the menu. When there is more than one page, page cycle buttons appear in the menu.
boolean array PLAYER_SELECTS_HERO //List of all players that are supposed to participate in hero selection (true = participates).
integer array TEAM_OF_PLAYER //Team of player, 1, 2 etc. 0 = no teams.
string array PLAYER_COLOR //Hex color string (including "|cff") for each player.
endglobals
function SetCategories takes nothing returns nothing
//Set the names of the categories in the hero selection menu. Starts at index 1. Set to null for no category caption.
set CATEGORY_NAMES[1] = "|cffffcc00Strength|r"
set CATEGORY_NAMES[2] = "|cffffcc00Agility|r"
set CATEGORY_NAMES[3] = "|cffffcc00Intelligence|r"
//Set the pages of the categories in the hero selection menu. Pages start at 1.
set PAGE_OF_CATEGORY[1] = 1
set PAGE_OF_CATEGORY[2] = 1
set PAGE_OF_CATEGORY[3] = 1
endfunction
function HeroSelectionInitPlayers takes nothing returns nothing
//Set the list of players for which hero selection is enabled (only if AUTO_SET_SELECTING_PLAYERS = false). It is disabled here because computer players must be added to the hero selection.
set PLAYER_SELECTS_HERO[0] = true
set PLAYER_SELECTS_HERO[1] = true
set PLAYER_SELECTS_HERO[2] = true
set PLAYER_SELECTS_HERO[3] = true
set PLAYER_SELECTS_HERO[4] = true
set PLAYER_SELECTS_HERO[5] = true
//Set the teams of players. Players with team 0 are enemies to all players.
set TEAM_OF_PLAYER[0] = 1
set TEAM_OF_PLAYER[1] = 1
set TEAM_OF_PLAYER[2] = 1
set TEAM_OF_PLAYER[3] = 2
set TEAM_OF_PLAYER[4] = 2
set TEAM_OF_PLAYER[5] = 2
endfunction
//=================================================================================================================================
globals
real array TEAM_FOREGROUND_HERO_X //x-Position of foreground hero.
real array TEAM_FOREGROUND_HERO_Y //y-Position of foreground hero.
real array TEAM_HERO_SELECTION_ANGLE //Hero selection camera yaw in degrees (0 = facing east, 90 = facing north etc.).
endglobals
function HeroSelectionInitArrays takes nothing returns nothing
//If you set SEPARATE_LOCATIONS_FOR_EACH_TEAM = true, initialize the above array variables here.
endfunction
function GetPlayerBackgroundHeroLocation takes integer playerIndex, integer whichSlot, integer numberOfPlayers, integer whichTeam, integer slotInTeam, integer teamSize returns location
local real deltaHorizontal
local real deltaVertical
//=================================================================================================================================
/*
CREATE_BACKGROUND_HEROES only.
Here you can customize the positions of the background heroes. Init loops through and calls this function for each player.
Input:
playerIndex
whichSlot The position of the player when enumerating all players for which hero selection is enabled.
numberOfPlayers The number of players for which hero selection is enabled.
whichTeam The team the player is assigned to. Discard if there are no teams.
slotInTeam The position of that player within his or her team. Discard if there are no teams.
teamSize The size of the player's team. Discard if there are no teams.
Output:
deltaVertical The distance between the background hero and the camera eye position along the camera viewing direction.
deltaHorizontal The offset between the background hero and the camera eye position perpendicular to the camera viewing direction.
*/
//==================================================================================================================================
//Example code:
local real angle
local real dist
local real BACKGROUND_HERO_OFFSET_BACKROW = 1425
local real BACKGROUND_HERO_OFFSET_FRONTROW = 1250
if teamSize == 1 then
set angle = 9
set dist = BACKGROUND_HERO_OFFSET_BACKROW
elseif slotInTeam == 1 then
set angle = 5
set dist = BACKGROUND_HERO_OFFSET_BACKROW
elseif slotInTeam == 2 then
set angle = 13
set dist = BACKGROUND_HERO_OFFSET_BACKROW
else
set angle = 9
set dist = BACKGROUND_HERO_OFFSET_FRONTROW
endif
if whichTeam == 2 then
set angle = -angle
endif
set deltaHorizontal = Sin(Deg2Rad(angle))*dist
set deltaVertical = Cos(Deg2Rad(angle))*dist
//==================================================================================================================================
return Location(deltaHorizontal, deltaVertical)
endfunction
//==================================================================================================================================
// F I N E - T U N I N G
//==================================================================================================================================
globals
//How hero selection looks like before it is enabled (you can ignore this if you plan on enabling hero selection right away).
constant boolean SHOW_MENU_BEFORE_ENABLED = false //Set to true if players should be able to see the hero selection menu before hero selection is enabled.
constant boolean PRE_SELECT_BEFORE_ENABLED = false //Set to true if players should be able to pre-select heroes before hero selection is enabled.
//Camera setup (ENFORCE_CAMERA).
constant real CAMERA_PITCH = 12 //Hero selection camera angle of attack in degrees. 0 = facing horizon, 90 = facing ground.
constant real CAMERA_DISTANCE = 1300 //Hero selection camera distance from camera target.
constant real CAMERA_TARGET_OFFSET = 500 //Distance between foreground hero and camera target. Positive = Camera target is behind the hero.
constant real CAMERA_PERPENDICULAR_OFFSET = 0 //Shifts the camera left (negative) or right (positive) with respect to the foreground hero.
constant real CAMERA_Z_OFFSET = 100 //Hero selection camera z-offset.
constant real CAMERA_FIELD_OF_VIEW = 70 //Hero selection camera field of view.
//Sounds and visuals of hero pre-selection (CREATE_FOREGROUND_HERO).
constant string PRESELECT_EFFECT = null //Special effect played on the hero's position for the triggering player when switching to a new hero during pre-selection. Set to null for no effect.
constant boolean PLAY_EMOTE_ON_PRESELECT = true //Set to true if the hero should play its selection emote when the player switches to that hero during pre-selection.
constant boolean PLAY_ANIMATION_ON_PRESELECT = true //Set to true if the hero should play the selection animation when the player switches to that hero during pre-selection.
constant boolean PHANTOM_HERO_WHEN_CANNOT_BE_PICKED = true //Set to true if a hero should be black and transparent when it is pre-selected but cannot be picked.
constant real FOREGROUND_HERO_Z = 0 //z-Position of foreground hero.
//Sounds and visuals on hero pick.
constant string PICK_EFFECT = "HolyLight.mdx" //Special effect played on the hero's position for the triggering player when picking a hero. Set to null for no effect.
constant string PICK_SOUND = "Sound\\Interface\\ItemReceived.flac" //Sound effect played for the triggering player when selecting a hero. Set to null for no sound.
constant boolean PLAY_EMOTE_ON_PICK = false //Set to true if a hero should play its selection emote when a player chooses that hero.
constant boolean PLAY_ANIMATION_ON_PICK = false //Set to true if a hero should play the selection animation when a player chooses that hero.
constant real PLAYER_PICK_ESCAPE_DELAY = 4.0 //Delay between selecting a hero and being kicked out of hero selection (ESCAPE_PLAYER_AFTER_SELECTING).
constant real FOREGROUND_HERO_FADEOUT_DELAY = 1.0 //The time it takes for the foreground hero to start fading out after being selected.
constant real FOREGROUND_HERO_FADEOUT_TIME = 1.5 //The time it takes for the foreground hero to fade out after being selected.
constant string OTHER_PLAYER_HERO_PICK_SOUND = "Sound\\Interface\\InGameChatWhat1.flac" //Sound played when another player picks a hero. Set to null for no sound.
//Text messages on hero pick.
constant boolean CREATE_TEXT_MESSAGE_ON_PICK = true //Set to true if a text message should be sent to all other players when a hero is picked (except to enemies when concealed).
constant real TEXT_MESSAGE_X_OFFSET = 0.35 //x-Offset of text messages from default. Text messages will still suck. Recommend using NeatMessages.
constant real TEXT_MESSAGE_Y_OFFSET = 0 //y-Offset of text messages from default.
constant boolean USE_HERO_PROPER_NAME = false //Instead of the hero's name, its proper name will be displayed in the text message. For example, "Uther" instead of "Paladin". Will temporarily create a hero to get the proper name.
constant boolean MESSAGE_EVEN_WHEN_CONCEALED = true //Set to true if players should still get a message notifying that a player has picked a hero even when it is concealed which hero was picked (CONCEAL_HERO_PICKS_FROM_ENEMIES).
constant boolean INCLUDE_PROGRESSION_IN_MESSAGE = false //Set to true if the displayed text message should include how many players have selected their hero.
//Sounds and visuals of background heroes (CREATE_BACKGROUND_HEROES).
constant boolean PLAYER_TEXT_TAGS = false //Create text tags that show the players' names over the background heroes.
constant real BACKGROUND_HERO_FADEIN_TIME = 1.0 //The time it takes for the background hero to fade in after being selected.
constant boolean PLAY_EMOTE_ON_BACKGROUND_HERO = true //Set to true if a hero should play its selection emote for all other players as it fades in.
constant boolean PLAY_ANIMATION_ON_BACKGROUND_HERO = true //Set to true if the background hero should play its selection animation as it fades in.
constant string BACKGROUND_HERO_FADEIN_EFFECT = "HolyLightRoyal.mdx" //Special effect played on the background hero as it fades in.
constant string BACKGROUND_HERO_SELF_HIGHLIGHT = "RadianceHoly.mdx" //Special effect added at the location of a player's own background hero to highlight it. Set to null for no highlight.
constant real BACKGROUND_HERO_HIGHLIGHT_Z = 70 //z-Position of background hero self highlight.
constant real BACKGROUND_HERO_FACING_POINT_OFFSET = -500 //Adjusts where the background heroes face. 0 = Background heroes will face the foreground hero. Negative value = Face a point closer to the camera. Positive value = Face a point further from the camera.
constant string CONCEALED_HERO_EFFECT = "Objects\\InventoryItems\\QuestionMark\\QuestionMark.mdl" //Model for the background hero seen by a player for which the hero pick was concealed.
//Last player picks a hero.
constant real LAST_PLAYER_SELECT_END_DELAY = 6.0 //The amount of time after the last player selects a hero and the hero selection ends (ignore if ESCAPE_PLAYER_AFTER_SELECTING)
constant boolean DELETE_BACKGROUND_HEROES_AFTER_END = false //Set to false if background heroes should not get removed when hero selection ends. For example, when a player repicks, they are still there.
//Layout of hero selection menu.
constant integer MENU_NUMBER_OF_COLUMNS = 2 //Number of hero buttons per row.
constant real MENU_X_LEFT = 0.0 //x-Position of left edge of hero selection menu.
constant real MENU_Y_TOP = 0.55 //y-Position of top edge of hero selection menu.
constant real MENU_BUTTON_SIZE = 0.039 //Size of individual hero buttons.
constant real MENU_LEFT_RIGHT_EDGE_GAP = 0.02 //Gap between left and right edges of menu and first and last buttons.
constant real MENU_TOP_EDGE_GAP = 0.015 //Gap between top edge of menu and first button/first category title.
constant real MENU_BOTTOM_EDGE_GAP = 0.02 //Gap between bottom edge of menu and last button.
constant real MENU_BUTTON_BUTTON_GAP = 0.005 //Gap between two individual hero buttons.
constant real MENU_CATEGORY_FONT_SIZE = 16 //Font size of category titles.
constant real MENU_CATEGORY_GAP = 0.028 //Gap between buttons of two different categories.
constant real MENU_CATEGORY_TITLE_Y = -0.001 //y-Position shift between category title and center of gap between categories.
constant real MENU_BORDER_TILE_SIZE = 0.03 //This rounds up the width and height of the menu to an integer multiple of the specified number. This is useful if you're using a tiled border texture, so that there's no discontinuity in the texture.
//Value should be equal to BackdropCornerSize in the HeroSelectionMenu.fdf. Set to 0 to disable.
constant real MENU_HEROES_RANDOM_GAP = 0.02 //Additional gap between last hero button and random pick button.
//Select button.
constant string SELECT_BUTTON_TEXT = "Accept" //The text in the select hero button at the bottom of the menu.
constant real SELECT_BUTTON_SCALE = 1.0 //The scale of the select hero button at the bottom of the menu.
constant real SELECT_BUTTON_WIDTH = 0.092 //The width of the select hero button at the bottom of the menu.
//Display of random options.
constant string RANDOM_HERO_TOOLTIP = "Choose a random hero."
constant string SUGGEST_RANDOM_TOOLTIP = "Suggest a random hero, but don't select it just yet."
constant string RANDOM_HERO_ICON = "ReplaceableTextures\\CommandButtons\\BTNRandomIncredibleIcon.blp"
constant string SUGGEST_RANDOM_ICON = "ReplaceableTextures\\CommandButtons\\BTNSelectHeroOn.blp"
constant boolean RANDOM_SELECT_CYCLE_STYLE = true //Set to true if the foreground hero should cycle randomly between different heroes while random hero is pre-selected. Set to false if a question mark should be shown.
constant real RANDOM_SELECT_CYCLE_INTERVAL = 0.2 //How fast the heroes are cycled through when random hero is pre-selected (RANDOM_SELECT_CYCLE_STYLE).
//Layout of ability preview buttons.
constant real HERO_ABILITY_PREVIEW_BUTTON_X = 0.0 //x-Position of topmost ability preview button relative to topright corner of menu.
constant real HERO_ABILITY_PREVIEW_BUTTON_Y = -0.016 //y-Position of topmost ability preview button relative to topright corner of menu.
constant real HERO_ABILITY_PREVIEW_BUTTON_SIZE = 0.025 //The size of the hero ability preview buttons that appear on the right side of the menu when pre-selecting a hero.
constant boolean ABILITY_BUTTON_HORIZONTAL_LAYOUT = false //Set to true if ability preview buttons should be arranged horizontally instead of vertically.
constant string HERO_ABILITY_LEVEL_DELIMITER = " - [" //To get the name of a hero ability without the level-text from the tooltip (such as "Stormbolt - [Level 1]" -> "Stormbolt"). This string is used to detect where the name of the ability ends. Change if ability names in tooltips are not correct. Set to null if not necessary.
//Page cycle buttons (when multiple pages are set).
constant string PAGE_CYCLE_BUTTON_STYLE = "EnvelopRandomButton" //Set the page cycle button style. Styles are found in documentation.
constant real MENU_PAGE_CYCLE_X_OFFSET = 0.0 //x-Position of the page cycle buttons relative to the auto-position. Positive = moves inward. Negative = moves outward.
constant real MENU_PAGE_CYCLE_Y_OFFSET = 0.0 //y-Position of the page cycle up button relative to the auto-position.
constant real MENU_PAGE_CYCLE_SCALE = 1.0 //Sets the size of the page cycle buttons relative to hero buttons (for icon style buttons) or the accept button (for text style buttons).
constant string MENU_PAGE_DOWN_ICON = "ReplaceableTextures\\CommandButtons\\BTNNagaBurrow.blp" //(for icon style buttons)
constant string MENU_PAGE_UP_ICON = "ReplaceableTextures\\CommandButtons\\BTNNagaUnBurrow.blp" //(for icon style buttons)
//Display of big caption.
constant string HERO_SELECTION_CAPTION = "Choose your Hero!" //The text displayed on the screen during hero selection. Set null to omit text.
constant real CAPTION_FONT_SIZE = 30 //Font size of hero selection caption.
constant real CAPTION_X = 0.4 //x-Position of caption center.
constant real CAPTION_Y = 0.41 //y-Position of caption center.
constant string CAPTION_COLOR_1 = "|cffffcc00" //Caption will cycle between color 1 and color 2. Set the same for no color cycling.
constant string CAPTION_COLOR_2 = "|cffffffff" //Caption will cycle between color 1 and color 2. Set the same for no color cycling.
constant integer CAPTION_ALPHA_1 = 255 //Caption will cycle between alpha 1 and alpha 2. Set the same for no alpha cycling.
constant integer CAPTION_ALPHA_2 = 255 //Caption will cycle between alpha 1 and alpha 2. Set the same for no alpha cycling.
constant real CAPTION_CYCLE_TIME = 4.0 //The time it takes for the caption to cycle between color 1/2 and alpha 1/2.
constant real CAPTION_FADEOUT_TIME = 2.0 //The time it takes for the caption to fade out after a player has picked a hero. Set to -1 for no fade out.
//Position of hero button and ability preview button mouse-over tooltips.
constant real TOOLTIP_LEFT_X = 0.51
constant real TOOLTIP_Y = 0.13
constant boolean TOOLTIP_LOCK_TOP = false //Set to true if TOOLTIP_Y should refer to top instead of bottom edge.
constant real TOOLTIP_WIDTH = 0.29
//Display of countdown timer.
constant string TIMER_TEXT = "Time Remaining: |cffffcc00" //Text before the time remaining display
constant string TIMER_BAN_PHASE_TEXT = "Ban Phase: |cffffcc00" //Text before the time remaining display during the ban phase.
constant real TIMER_FONT_SIZE = 11 //Font size of the time remaining display.
constant real TIMER_X = 0.4 //x-Position of center of the time remaining display.
constant real TIMER_Y = 0.59 //y-Position of the center of the time remaining display.
//Shadows of heroes.
constant boolean CREATE_SHADOWS = false //Create shadows with destructables for heroes (since they are special effects and don't have shadows). Import destructables from showcase map to enable.
constant integer SHADOW_DESTRUCTABLE_ID = 'Dsha' //Destructable id of the shadow that's created for heroes.
constant integer NO_SHADOW_DESTRUCTABLE_ID = 'Dnsh' //Dummy destructable without a shadow.
endglobals
//=================================================================================================================================
function HeroSelectionSetPlayerColors takes nothing returns nothing
//Set the colors of players shown for their names in text messages.
set PLAYER_COLOR[0] = "|cffff0402"
set PLAYER_COLOR[1] = "|cff1052ff"
set PLAYER_COLOR[2] = "|cff1BE6BA"
set PLAYER_COLOR[3] = "|cff8530b1"
set PLAYER_COLOR[4] = "|cfffffc00"
set PLAYER_COLOR[5] = "|cffff8a0d"
set PLAYER_COLOR[6] = "|cff20bf00"
set PLAYER_COLOR[7] = "|cffE35BAF"
set PLAYER_COLOR[8] = "|cff949697"
set PLAYER_COLOR[9] = "|cff7EBFF1"
set PLAYER_COLOR[10] = "|cff106247"
set PLAYER_COLOR[11] = "|cff4F2B05"
set PLAYER_COLOR[12] = "|cff9C0000"
set PLAYER_COLOR[13] = "|cff0000C2"
set PLAYER_COLOR[14] = "|cff00EBEB"
set PLAYER_COLOR[15] = "|cffBE00FF"
set PLAYER_COLOR[16] = "|cffECCC86"
set PLAYER_COLOR[17] = "|cffF7A48B"
set PLAYER_COLOR[18] = "|cffBFFF80"
set PLAYER_COLOR[19] = "|cffDBB8EC"
set PLAYER_COLOR[20] = "|cff4F4F55"
set PLAYER_COLOR[21] = "|cffECF0FF"
set PLAYER_COLOR[22] = "|cff00781E"
set PLAYER_COLOR[23] = "|cffA46F34"
endfunction
endlibrary
library HeroSelection requires HeroSelectionConfig, HeroDeclaration, optional NeatMessages
globals
private effect array selectedHeroGlow
private effect array backgroundHeroGlow
private destructable array backgroundHeroShadow
private destructable foregroundHeroShadow = null
private texttag array backgroundHeroTextTag
private integer array preselectedHeroIndex
private integer array pickedHeroIndex
private integer localPlayerId //asynchronous
private boolean array heroIndexWasPicked
private boolean array heroIndexWasBanned[1][24]
private real localForegroundHeroX //asynchronous
private real localForegroundHeroY //asynchronous
private real localHeroSelectionAngle //asynchronous
private integer currentPage = 1 //asynchronous
private framehandle captionFrame = null
private framehandle timerFrame = null
private framehandle heroSelectionMenu = null
private framehandle array heroSelectionButton
private framehandle array heroSelectionButtonIcon
private framehandle heroSelectionButtonHighlight
private framehandle array heroSelectionButtonIconClicked
private framehandle array heroSelectionButtonTooltip
private framehandle array heroSelectionButtonTooltipTitle
private framehandle array heroSelectionButtonTooltipText
private framehandle array heroSelectionAbility
private framehandle array heroSelectionAbilityHover
private framehandle array heroSelectionAbilityTooltip
private framehandle array heroSelectionAbilityTooltipTitle
private framehandle array heroSelectionAbilityTooltipText
private framehandle array heroSelectionCategory
private framehandle heroAcceptButton = null
private framehandle heroBanButton = null
private camerasetup heroSelectionCamera = CreateCameraSetup()
private hashtable hash = InitHashtable()
private timer lockCameraTimer = CreateTimer()
private timer captionTimer = CreateTimer()
private timer countdownTimer = CreateTimer()
private timer countdownUpdateTimer = CreateTimer()
private trigger heroSelectionButtonTrigger
private trigger pageCycleTrigger
private framehandle fullScreenFrame
private framehandle fullScreenParent
private boolean isForcedSelect = false
private real captionAlphaMultiplier = 1
private constant real TOOLTIP_BASE_HEIGHT = 0.032
private real tooltipLeftXLocal
private constant string HEX_STRING = "0123456789abcdef"
private integer r1
private integer r2
private integer g1
private integer g2
private integer b1
private integer b2
private location moveableLoc = Location(0,0)
private integer storePlayerIndex
private integer storeHeroIndex
private real GARBAGE_DUMP_X
private real GARBAGE_DUMP_Y
private integer NUMBER_OF_CATEGORIES = 0
private integer NUMBER_OF_PAGES = 1
private integer NUMBER_OF_ABILITY_FRAMES = 0
private integer NUMBER_OF_TEAMS = 0
private integer RANDOM_HERO = 0
private integer SUGGEST_RANDOM = 0
private integer PAGE_DOWN = 0
private integer PAGE_UP = 0
endglobals
private function interface playerCallback takes player whichPlayer returns nothing
private function interface playerHeroCallback takes player whichPlayer, Hero whichHero returns nothing
private function interface onPickCallback takes player whichPlayer, Hero whichHero, boolean wasRandom, boolean wasRepick returns nothing
private function interface playerHeroHeroCallback takes player whichPlayer, Hero oldHero, Hero newHero returns nothing
private function interface noArgCallback takes nothing returns nothing
struct Hero
//Fields that must be set.
//===================================
integer array abilities[9]
boolean array isNonHeroAbility[9]
integer unitId
string tooltip
integer tooltipAbility
string selectEmote
animtype selectAnim
subanimtype selectSubAnim
real selectAnimLength
integer category
boolean needsHeroGlow
boolean unavailable
boolean array unavailableToTeam[5]
//===================================
readonly string modelPath
readonly integer index
readonly string iconPath
readonly real scalingValue
readonly string name
readonly integer red
readonly integer green
readonly integer blue
readonly static integer numHeroes = 0
readonly static Hero array list
static method create takes nothing returns Hero
local Hero this = Hero.allocate()
set numHeroes = numHeroes + 1
set .index = numHeroes
set list[numHeroes] = this
return this
endmethod
method GetValues takes nothing returns nothing
local unit tempUnit
local item tempItem
set tempUnit = CreateUnit( Player(PLAYER_NEUTRAL_PASSIVE) , .unitId , GARBAGE_DUMP_X , GARBAGE_DUMP_Y , 0 )
set .name = GetUnitName(tempUnit)
set .scalingValue = BlzGetUnitRealField( tempUnit , UNIT_RF_SCALING_VALUE )
set .red = BlzGetUnitIntegerField(tempUnit, UNIT_IF_TINTING_COLOR_RED)
set .green = BlzGetUnitIntegerField(tempUnit, UNIT_IF_TINTING_COLOR_GREEN)
set .blue = BlzGetUnitIntegerField(tempUnit, UNIT_IF_TINTING_COLOR_BLUE)
set .iconPath = BlzGetAbilityIcon(GetUnitTypeId(tempUnit))
call RemoveUnit(tempUnit)
set tempUnit = null
set tempItem = CreateItem('afac', GARBAGE_DUMP_X, GARBAGE_DUMP_Y)
call BlzSetItemSkin(tempItem, .unitId)
set .modelPath = BlzGetItemStringField(tempItem, ITEM_SF_MODEL_USED)
call RemoveItem(tempItem)
set tempItem = null
endmethod
endstruct
//==========================================================================================================================================================
//Utility
//==========================================================================================================================================================
private function InitFullScreenParents takes nothing returns nothing
local framehandle board
local integer localClientHeight = BlzGetLocalClientHeight()
call CreateLeaderboardBJ(bj_FORCE_ALL_PLAYERS, "title")
set board = BlzGetFrameByName("Leaderboard", 0)
call BlzFrameSetSize(board, 0, 0)
call BlzFrameSetVisible(BlzGetFrameByName("LeaderboardBackdrop", 0), false)
call BlzFrameSetVisible(BlzGetFrameByName("LeaderboardTitle", 0), false)
set fullScreenParent = BlzCreateFrameByType("FRAME", "fullScreenParent", board, "", 0)
set fullScreenFrame = BlzCreateFrameByType("FRAME", "fullscreen", fullScreenParent, "", 0)
call BlzFrameSetVisible(fullScreenFrame, false)
if localClientHeight > 0 then
call BlzFrameSetSize(fullScreenFrame, BlzGetLocalClientWidth()/I2R(localClientHeight)*0.6, 0.6)
else
call BlzFrameSetSize(fullScreenFrame, 16.0/9.0*0.6, 0.6)
endif
call BlzFrameSetAbsPoint(fullScreenFrame, FRAMEPOINT_BOTTOM, 0.4, 0)
endfunction
private function CheckForCrash takes nothing returns nothing
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
local timer crashTimer = GetExpiredTimer()
local boolean didCrash = LoadBoolean(hash, GetHandleId(crashTimer), 0)
local string functionName
if didCrash then
set functionName = LoadStr(hash, GetHandleId(crashTimer), 1)
call BJDebugMsg("|cffff0000Warning:|r " + functionName + " function crashed...")
endif
call FlushChildHashtable(hash, GetHandleId(crashTimer))
call DestroyTimer(crashTimer)
set crashTimer = null
endif
endfunction
private function InitCrashCheck takes string functionName returns nothing
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
local timer crashTimer = CreateTimer()
call TimerStart(crashTimer, 0.01, false, function CheckForCrash)
call SaveBoolean(hash, GetHandleId(crashTimer), 0, true)
call SaveStr(hash, GetHandleId(crashTimer), 1, functionName)
call SaveTimerHandle(hash, StringHash(functionName), 0, crashTimer)
set crashTimer = null
endif
endfunction
private function NoCrash takes string functionName returns nothing
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
local timer crashTimer = LoadTimerHandle(hash, StringHash(functionName), 0)
call SaveBoolean(hash, GetHandleId(crashTimer), 0, false)
set crashTimer = null
endif
endfunction
private function GetClockString takes real rawSeconds returns string
local integer seconds
local integer minutes = R2I(rawSeconds) / 60
local string clock
set seconds = ModuloInteger(R2I(rawSeconds), 60)
set clock = I2S(minutes) + ":"
if seconds >= 10 then
set clock = clock + I2S(seconds)
else
set clock = clock + "0" + I2S(seconds)
endif
return clock
endfunction
private function TimeExpires takes nothing returns nothing
local noArgCallback onExpire = HeroSelectionOnExpire
call ExecuteFunc("HeroSelectionForceEnd")
call PauseTimer(countdownUpdateTimer)
call BlzFrameSetText(timerFrame, TIMER_TEXT + GetClockString(0))
call onExpire.evaluate()
endfunction
private function GetLocZ takes real x, real y returns real
call MoveLocation(moveableLoc, x, y)
return GetLocationZ(moveableLoc)
endfunction
private function TimerCounterPlus takes timer t returns integer
local integer counter = LoadInteger( hash , GetHandleId(t) , 0 )
set counter = counter + 1
call SaveInteger( hash , GetHandleId(t) , 0 , counter )
return counter
endfunction
private function PlaySoundLocal takes string soundPath, boolean localPlayerCanHearSound returns nothing
local real volume
local sound s = CreateSound( soundPath , FALSE, FALSE, FALSE, 10, 10, "DefaultEAXON")
call SetSoundChannel(s, 0)
if localPlayerCanHearSound then
set volume = 100
else
set volume = 0
endif
call SetSoundVolumeBJ( s , volume )
call StartSound(s)
call KillSoundWhenDone(s)
set s = null
endfunction
private function GetPickedHeroDisplayedName takes integer heroIndex, boolean capitalize returns string
local unit tempUnit
local string name
static if USE_HERO_PROPER_NAME then
set tempUnit = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), Hero.list[heroIndex].unitId, GARBAGE_DUMP_X, GARBAGE_DUMP_Y, 0)
set name = "|cffffcc00" + GetHeroProperName(tempUnit) + "|r"
call RemoveUnit(tempUnit)
set tempUnit = null
return name
else
if capitalize then
return "The |cffffcc00" + Hero.list[heroIndex].name + "|r"
else
return "the |cffffcc00" + Hero.list[heroIndex].name + "|r"
endif
endif
endfunction
//==========================================================================================================================================================
//Hero Menu
//==========================================================================================================================================================
private function HeroCanBePicked takes integer heroIndex, integer P returns boolean
return heroIndex != 0 and not ((heroIndexWasPicked[heroIndex] and not HERO_CAN_BE_PICKED_MULTIPLE_TIMES) or Hero.list[heroIndex].unavailable or heroIndexWasBanned[heroIndex][P])
endfunction
private function GetHeroAbilityName takes integer id returns string
local string tooltip = BlzGetAbilityTooltip(id, 0)
local integer length = StringLength(tooltip)
local integer delimLength = StringLength(HERO_ABILITY_LEVEL_DELIMITER)
local integer i = 1
local integer j
if HERO_ABILITY_LEVEL_DELIMITER == null or HERO_ABILITY_LEVEL_DELIMITER == "" then
return tooltip
endif
loop
exitwhen i > length - delimLength
set j = 0
loop
exitwhen SubString(tooltip, i+j, i+j+1) != SubString(HERO_ABILITY_LEVEL_DELIMITER, j, j+1)
if j == delimLength - 1 then
return SubString(tooltip, 0, i)
endif
set j = j + 1
endloop
set i = i + 1
endloop
return tooltip
endfunction
private function GetRandomHero takes integer currentSelection, integer P returns integer
local integer array heroesAvailable
local integer numChars = 0
local integer i
local boolean currentSelectionAvailable = false
local integer currentSelectionChar
set i = 1
loop
exitwhen i > NUMBER_OF_HEROES
if HeroCanBePicked(i, P) and not Hero.list[i].unavailableToTeam[TEAM_OF_PLAYER[P]] then
set numChars = numChars + 1
set heroesAvailable[numChars] = i
if currentSelection == i then
set currentSelectionAvailable = true
set currentSelectionChar = numChars
endif
endif
set i = i + 1
endloop
if currentSelectionAvailable and currentSelection != 0 then
//Return random hero not currently selected.
return heroesAvailable[ModuloInteger( currentSelectionChar + GetRandomInt(0,numChars-2) , numChars ) + 1]
else
return heroesAvailable[GetRandomInt(1,numChars)]
endif
endfunction
private function SetButtonFrames takes integer whichButton returns nothing
set heroSelectionButtonIcon[whichButton] = BlzFrameGetChild(heroSelectionButton[whichButton],0)
set heroSelectionButtonIconClicked[whichButton] = BlzFrameGetChild(heroSelectionButton[whichButton],1)
call BlzFrameClearAllPoints(heroSelectionButtonIconClicked[whichButton])
call BlzFrameSetPoint(heroSelectionButtonIconClicked[whichButton], FRAMEPOINT_BOTTOMLEFT, heroSelectionButton[whichButton], FRAMEPOINT_BOTTOMLEFT, 0.001, 0.001)
call BlzFrameSetPoint(heroSelectionButtonIconClicked[whichButton], FRAMEPOINT_TOPRIGHT, heroSelectionButton[whichButton], FRAMEPOINT_TOPRIGHT, -0.001, -0.001)
endfunction
private function SetButtonTooltip takes integer whichButton, string whichTitle, string whichTooltip returns nothing
set heroSelectionButtonTooltip[whichButton] = BlzCreateFrame("CustomTooltip", heroSelectionButton[whichButton], 0, 0)
if TOOLTIP_LOCK_TOP then
call BlzFrameSetAbsPoint( heroSelectionButtonTooltip[whichButton] , FRAMEPOINT_TOPLEFT , TOOLTIP_LEFT_X , TOOLTIP_Y )
else
call BlzFrameSetAbsPoint( heroSelectionButtonTooltip[whichButton] , FRAMEPOINT_BOTTOMLEFT , TOOLTIP_LEFT_X , TOOLTIP_Y )
endif
call BlzFrameSetTooltip( heroSelectionButton[whichButton] , heroSelectionButtonTooltip[whichButton] )
set heroSelectionButtonTooltipTitle[whichButton] = BlzFrameGetChild(heroSelectionButtonTooltip[whichButton],0)
set heroSelectionButtonTooltipText[whichButton] = BlzFrameGetChild(heroSelectionButtonTooltip[whichButton],1)
call BlzFrameSetText( heroSelectionButtonTooltipTitle[whichButton] , whichTitle )
call BlzFrameSetText( heroSelectionButtonTooltipText[whichButton] , whichTooltip )
call BlzFrameSetSize(heroSelectionButtonTooltipText[whichButton] , TOOLTIP_WIDTH - 0.01 , 0.0 )
call BlzFrameSetSize(heroSelectionButtonTooltip[whichButton] , TOOLTIP_WIDTH , BlzFrameGetHeight(heroSelectionButtonTooltipText[whichButton]) + TOOLTIP_BASE_HEIGHT)
endfunction
private function SetButtonTextures takes integer whichButton, boolean disabledTexture returns nothing
local integer i
local integer stringLength
local string disabledPath
local string path
if whichButton <= NUMBER_OF_HEROES then
set path = Hero.list[whichButton].iconPath
elseif whichButton == RANDOM_HERO then
set path = RANDOM_HERO_ICON
elseif whichButton == SUGGEST_RANDOM then
set path = SUGGEST_RANDOM_ICON
elseif whichButton == PAGE_DOWN then
set path = MENU_PAGE_DOWN_ICON
else
set path = MENU_PAGE_UP_ICON
endif
if not disabledTexture then
call BlzFrameSetTexture(heroSelectionButtonIcon[whichButton] , path , 0 , true)
call BlzFrameSetTexture(heroSelectionButtonIconClicked[whichButton] , path , 0 , true)
else
set stringLength = StringLength(path)
set i = 0
loop
exitwhen i > stringLength - 3
if SubString(path, i, i+1) == "B" and SubString(path, i+1, i+2) == "T" and SubString(path, i+2, i+3) == "N" then
set disabledPath = "ReplaceableTextures\\CommandButtonsDisabled\\DISBTN" + SubString(path, i+3, stringLength)
call BlzFrameSetTexture(heroSelectionButtonIcon[whichButton] , disabledPath, 0 , true)
call BlzFrameSetTexture(heroSelectionButtonIconClicked[whichButton] , disabledPath, 0 , true)
return
endif
set i = i + 1
endloop
endif
endfunction
//==========================================================================================================================================================
//Caption
//==========================================================================================================================================================
private function TextColor takes nothing returns nothing
local integer counter = TimerCounterPlus(GetExpiredTimer())
local real colorState = (1 + Cos(2*bj_PI*counter/(CAPTION_CYCLE_TIME/0.02)))/2
local integer r = R2I((1 - colorState)*r1 + colorState*r2)
local integer g = R2I((1 - colorState)*g1 + colorState*g2)
local integer b = R2I((1 - colorState)*b1 + colorState*b2)
local integer array c
local integer i
local string colorString = "|cff"
set c[1] = r/16
set c[2] = r - 16*c[1]
set c[3] = g/16
set c[4] = g - 16*c[3]
set c[5] = b/16
set c[6] = b - 16*c[5]
set i = 1
loop
exitwhen i > 6
set colorString = colorString + SubString(HEX_STRING, c[i], c[i] + 1)
set i = i + 1
endloop
if CAPTION_FADEOUT_TIME != -1 then
if playerHasHero[localPlayerId] then
if CAPTION_FADEOUT_TIME > 0 then
set captionAlphaMultiplier = RMaxBJ(0, captionAlphaMultiplier - 1/(50*CAPTION_FADEOUT_TIME))
else
set captionAlphaMultiplier = 0
endif
endif
endif
call BlzFrameSetText(captionFrame, colorString + HERO_SELECTION_CAPTION + "|r")
call BlzFrameSetAlpha(captionFrame, R2I(captionAlphaMultiplier*((1 - colorState)*CAPTION_ALPHA_1 + colorState*CAPTION_ALPHA_2)))
endfunction
private function HexToInt takes string hexString returns integer
local integer i = 0
local integer int = 0
local string firstChar = StringCase(SubString(hexString, 0, 1), false)
local string secondChar = StringCase(SubString(hexString, 1, 2), false)
local boolean firstCharFound = false
local boolean secondCharFound = false
set i = 0
loop
exitwhen i > 15 or (firstCharFound and secondCharFound)
if not firstCharFound and firstChar == SubString(HEX_STRING, i, i + 1) then
set int = int + 16*i
set firstCharFound = true
endif
if not secondCharFound and secondChar == SubString(HEX_STRING, i, i + 1) then
set int = int + i
set secondCharFound = true
endif
set i = i + 1
endloop
return int
endfunction
private function AnimateCaption takes nothing returns nothing
local integer i
local string s
local integer j
//Get RGB values of CAPTION_COLOR_1 and CAPTION_COLO_2.
set r1 = HexToInt(SubString(CAPTION_COLOR_1, 4, 6))
set g1 = HexToInt(SubString(CAPTION_COLOR_1, 6, 8))
set b1 = HexToInt(SubString(CAPTION_COLOR_1, 8, 10))
set r2 = HexToInt(SubString(CAPTION_COLOR_2, 4, 6))
set g2 = HexToInt(SubString(CAPTION_COLOR_2, 6, 8))
set b2 = HexToInt(SubString(CAPTION_COLOR_2, 8, 10))
call TimerStart( captionTimer , 0.02 , true , function TextColor )
call SaveInteger( hash , GetHandleId(captionTimer) , 0 , 0 )
endfunction
//==========================================================================================================================================================
//Hero Effects
//==========================================================================================================================================================
private function FadeInBackgroundHero takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer counter = TimerCounterPlus(t)
local integer P = LoadInteger( hash , GetHandleId(t) , 1 )
local boolean concealed = LoadBoolean( hash , GetHandleId(t) , 2 )
local Hero whichHero = Hero.list[pickedHeroIndex[P]]
//Hero for owner fades in later than for other players since it's still in the front.
local integer ownerDelay = R2I(50*(FOREGROUND_HERO_FADEOUT_DELAY + FOREGROUND_HERO_FADEOUT_TIME)/BACKGROUND_HERO_FADEIN_TIME)
local string modelPath
if localPlayerId != P and counter <= 51 then
if concealed then
call BlzSetSpecialEffectAlpha( backgroundHero[P] , R2I(2.5*counter) )
if whichHero.needsHeroGlow then
call BlzSetSpecialEffectAlpha( backgroundHeroGlow[P], R2I(2.5*counter) )
endif
else
call BlzSetSpecialEffectAlpha( backgroundHero[P] , 5*counter )
if whichHero.needsHeroGlow then
call BlzSetSpecialEffectAlpha( backgroundHeroGlow[P], 5*counter )
endif
endif
elseif localPlayerId == P and counter > ownerDelay then
call BlzSetSpecialEffectAlpha( backgroundHero[P] , 5*(counter - ownerDelay))
if whichHero.needsHeroGlow then
call BlzSetSpecialEffectAlpha( backgroundHeroGlow[P], 5*(counter - ownerDelay))
endif
endif
static if CREATE_SHADOWS then
if counter == ownerDelay then
call RemoveDestructable(backgroundHeroShadow[P])
set backgroundHeroShadow[P] = CreateDestructable(SHADOW_DESTRUCTABLE_ID, BACKGROUND_HERO_X[P], BACKGROUND_HERO_Y[P], 0, 1, 0)
endif
endif
if counter == 51 + ownerDelay then
if BACKGROUND_HERO_SELF_HIGHLIGHT != null then
if localPlayerId == P then
set modelPath = BACKGROUND_HERO_SELF_HIGHLIGHT
else
set modelPath = ""
endif
set backgroundHeroHighlight[P] = AddSpecialEffect(modelPath, BACKGROUND_HERO_X[P], BACKGROUND_HERO_Y[P])
call BlzSetSpecialEffectZ(backgroundHeroHighlight[P], GetLocZ(BACKGROUND_HERO_X[P], BACKGROUND_HERO_Y[P]) + BACKGROUND_HERO_HIGHLIGHT_Z)
endif
call FlushChildHashtable( hash , GetHandleId(t) )
call DestroyTimer(t)
endif
set t = null
endfunction
private function FadeoutForegroundHero takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer counter = TimerCounterPlus(t)
local integer P = LoadInteger( hash , GetHandleId(t) , 1 )
if counter < 51 and FOREGROUND_HERO_FADEOUT_TIME > 0 then
call BlzSetSpecialEffectAlpha( selectedHero[P] , 255 - 5*counter )
call BlzSetSpecialEffectAlpha( selectedHeroGlow[P] , 255 - 5*counter )
call TimerStart( t , FOREGROUND_HERO_FADEOUT_TIME/50.0 , false , function FadeoutForegroundHero )
else
call BlzSetSpecialEffectPosition( selectedHero[P] , GARBAGE_DUMP_X , GARBAGE_DUMP_Y , 0 )
call DestroyEffect( selectedHero[P] )
call DestroyEffect( selectedHeroGlow[P] )
call FlushChildHashtable( hash , GetHandleId(t) )
call DestroyTimer(t)
endif
set t = null
endfunction
private function ResetAnimation takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer id = GetHandleId(t)
local integer P = LoadInteger( hash , id , 0 )
local integer heroIndex = LoadInteger( hash , id , 1 )
local boolean animateBackgroundHero = LoadBoolean( hash , id , 2)
if animateBackgroundHero then
if localPlayerId != P then
call BlzSpecialEffectClearSubAnimations(backgroundHero[P])
call BlzPlaySpecialEffect( backgroundHero[P] , ANIM_TYPE_STAND )
endif
else
if preselectedHeroIndex[P] == heroIndex then
call BlzSpecialEffectClearSubAnimations(selectedHero[P])
call BlzPlaySpecialEffect( selectedHero[P] , ANIM_TYPE_STAND )
endif
endif
call FlushChildHashtable( hash , GetHandleId(t) )
call DestroyTimer(t)
set t = null
endfunction
private function PlayHeroAnimation takes integer P, integer heroIndex, boolean animateBackgroundHero returns nothing
local timer t = CreateTimer()
local integer id = GetHandleId(t)
if animateBackgroundHero then
if localPlayerId != P and (not CONCEAL_HERO_PICKS_FROM_ENEMIES or (TEAM_OF_PLAYER[localPlayerId] == TEAM_OF_PLAYER[P] and TEAM_OF_PLAYER[P] != 0)) then
call BlzSpecialEffectAddSubAnimation( backgroundHero[P] , Hero.list[heroIndex].selectSubAnim )
call BlzPlaySpecialEffect( backgroundHero[P] , Hero.list[heroIndex].selectAnim )
endif
else
call BlzSpecialEffectAddSubAnimation( selectedHero[P] , Hero.list[heroIndex].selectSubAnim )
call BlzPlaySpecialEffect( selectedHero[P] , Hero.list[heroIndex].selectAnim )
endif
call TimerStart( t , Hero.list[heroIndex].selectAnimLength , false , function ResetAnimation )
call SaveInteger( hash , id , 0 , P )
call SaveInteger( hash , id , 1 , heroIndex )
call SaveBoolean( hash , id , 2 , animateBackgroundHero)
set t = null
endfunction
private function CreateNewForegroundHero takes integer P, integer heroIndex returns nothing
local string modelPath = ""
local string glowPath = ""
local real locZ
local Hero whichHero = Hero.list[heroIndex]
if localPlayerId == P then
if heroIndex == RANDOM_HERO then
set modelPath = "Objects\\InventoryItems\\QuestionMark\\QuestionMark.mdl"
else
set modelPath = whichHero.modelPath
endif
if whichHero.needsHeroGlow then
set glowPath = "GeneralHeroGlow.mdx"
endif
endif
call BlzSetSpecialEffectPosition( selectedHero[P] , GARBAGE_DUMP_X , GARBAGE_DUMP_Y , 0 )
call BlzSetSpecialEffectTimeScale( selectedHero[P] , 9999)
call BlzSetSpecialEffectScale(selectedHero[P], 0)
call DestroyEffect(selectedHero[P])
call DestroyEffect(selectedHeroGlow[P])
set selectedHero[P] = AddSpecialEffect( modelPath , localForegroundHeroX , localForegroundHeroY )
set selectedHeroGlow[P] = AddSpecialEffect( glowPath , localForegroundHeroX , localForegroundHeroY )
set locZ = GetLocZ(localForegroundHeroX, localForegroundHeroY)
call BlzSetSpecialEffectZ( selectedHero[P], locZ + FOREGROUND_HERO_Z )
call BlzSetSpecialEffectZ( selectedHeroGlow[P], locZ + FOREGROUND_HERO_Z )
call BlzSetSpecialEffectYaw( selectedHero[P] , Deg2Rad(localHeroSelectionAngle) + bj_PI )
call BlzSetSpecialEffectColorByPlayer( selectedHero[P] , Player(P) )
call BlzSetSpecialEffectColorByPlayer( selectedHeroGlow[P] , Player(P) )
if heroIndex != RANDOM_HERO then
call BlzSetSpecialEffectScale( selectedHero[P] , whichHero.scalingValue )
endif
if PHANTOM_HERO_WHEN_CANNOT_BE_PICKED and (heroSelectionDisabledForPlayer[P] or not HeroCanBePicked(heroIndex, P)) then
call BlzSetSpecialEffectColor( selectedHero[P], 0, 0, 0 )
call BlzSetSpecialEffectAlpha( selectedHero[P], 128 )
else
call BlzSetSpecialEffectColor( selectedHero[P] , whichHero.red , whichHero.green , whichHero.blue)
endif
endfunction
private function DeleteBackgroundHero takes integer P returns nothing
call BlzSetSpecialEffectPosition(backgroundHero[P], GARBAGE_DUMP_X, GARBAGE_DUMP_Y, 0)
call DestroyEffect(backgroundHero[P])
call DestroyEffect(backgroundHeroGlow[P])
call DestroyEffect(backgroundHeroHighlight[P])
static if CREATE_SHADOWS then
call RemoveDestructable(backgroundHeroShadow[P])
endif
static if PLAYER_TEXT_TAGS then
call DestroyTextTag(backgroundHeroTextTag[P])
endif
endfunction
//==========================================================================================================================================================
//Miscellaneous.
//==========================================================================================================================================================
private function UpdateTimerFrame takes nothing returns nothing
if inBanPhase then
call BlzFrameSetText(timerFrame, TIMER_BAN_PHASE_TEXT + GetClockString(TimerGetRemaining(countdownTimer)))
else
call BlzFrameSetText(timerFrame, TIMER_TEXT + GetClockString(TimerGetRemaining(countdownTimer)))
endif
endfunction
private function LockSelecterCamera takes nothing returns nothing
local integer i = 1
loop
exitwhen i > numSelectingPlayers
if isInHeroSelection[playerNumberOfSlot[i]] then
call CameraSetupApplyForPlayer( true, heroSelectionCamera, Player(playerNumberOfSlot[i]), 0 )
endif
set i = i + 1
endloop
endfunction
//==========================================================================================================================================================
//End hero selection.
//==========================================================================================================================================================
private function EscapePlayer takes player whichPlayer returns nothing
local integer P = GetPlayerId(whichPlayer)
local playerCallback onEscape = HeroSelectionOnEscape
local framehandle consoleUIBackdrop = BlzGetFrameByName("ConsoleUIBackdrop",0)
local framehandle bottomUI = BlzGetFrameByName("ConsoleBottomBar", 0)
local framehandle topUI = BlzGetFrameByName("ConsoleTopBar", 0)
if not isInHeroSelection[P] then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to escape player who is not in hero selection...")
endif
return
endif
call InitCrashCheck("EscapePlayer")
set isInHeroSelection[P] = false
set preselectedHeroIndex[P] = 0
set isRepicking[P] = false
set playerHasBan[P] = false
set numPlayersInSelection = numPlayersInSelection - 1
call DestroyEffect(selectedHero[P])
call ResetToGameCameraForPlayer(whichPlayer, 0)
if localPlayerId == P then
call BlzFrameSetVisible(heroSelectionMenu, false)
call BlzFrameSetVisible(captionFrame, false)
call BlzFrameSetVisible(timerFrame, false)
static if HIDE_GAME_UI then
call BlzHideOriginFrames(false)
call BlzFrameSetVisible(consoleUIBackdrop, true)
call BlzFrameSetVisible(bottomUI, true)
call BlzFrameSetVisible(topUI, true)
endif
endif
call onEscape.evaluate(whichPlayer)
call NoCrash("EscapePlayer")
endfunction
private function EndHeroSelection takes nothing returns nothing
local integer i = 1
local integer P
local noArgCallback onFinal = HeroSelectionOnFinal
call InitCrashCheck("EndHeroSelection")
call PauseTimer(lockCameraTimer)
call PauseTimer(countdownTimer)
call PauseTimer(countdownUpdateTimer)
static if not ESCAPE_PLAYER_AFTER_SELECTING then
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
call EscapePlayer(Player(P))
set i = i + 1
endloop
endif
set i = 1
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
call BlzSetSpecialEffectPosition(selectedHero[P], GARBAGE_DUMP_X, GARBAGE_DUMP_Y, 0)
call DestroyEffect(selectedHero[P])
call DestroyEffect(selectedHeroGlow[P])
static if DELETE_BACKGROUND_HEROES_AFTER_END then
call BlzSetSpecialEffectPosition(backgroundHero[P], GARBAGE_DUMP_X, GARBAGE_DUMP_Y, 0)
call DeleteBackgroundHero(P)
endif
set i = i + 1
endloop
static if CREATE_SHADOWS then
call RemoveDestructable(foregroundHeroShadow)
endif
call onFinal.evaluate()
call NoCrash("EndHeroSelection")
endfunction
private function EscapePlayerCaller takes nothing returns nothing
local timer t = GetExpiredTimer()
call EscapePlayer(Player(LoadInteger(hash, GetHandleId(t), 0)))
call FlushChildHashtable(hash, GetHandleId(t))
call DestroyTimer(t)
set t = null
static if ESCAPE_PLAYER_AFTER_SELECTING then
if numPlayersInSelection == 0 then
call EndHeroSelection()
endif
endif
endfunction
private function EndHeroSelectionCaller takes nothing returns nothing
local timer t = GetExpiredTimer()
call EndHeroSelection()
call DestroyTimer(t)
set t = null
endfunction
private function OnPlayerLeave takes nothing returns nothing
local player whichPlayer = GetTriggerPlayer()
local integer P = GetPlayerId(whichPlayer)
local integer i = 1
local integer j = 1
local playerCallback onLeave = HeroSelectionOnLeave
call InitCrashCheck("OnPlayerLeave")
loop
exitwhen i > numSelectingPlayers
if P == playerNumberOfSlot[i] then
set j = i
loop
exitwhen j > numSelectingPlayers - 1
set playerNumberOfSlot[j] = playerNumberOfSlot[j+1]
set j = j + 1
endloop
exitwhen true
endif
set i = i + 1
endloop
set numSelectingPlayers = numSelectingPlayers - 1
if isInHeroSelection[P] then
call onLeave.evaluate(whichPlayer)
set numPlayersInSelection = numPlayersInSelection - 1
set isInHeroSelection[P] = false
endif
if playerHasHero[P] then
set numPlayersWithHero = numPlayersWithHero - 1
set playerHasHero[P] = false
call BlzSetSpecialEffectPosition(selectedHero[P], GARBAGE_DUMP_X, GARBAGE_DUMP_Y, 0)
call DestroyEffect(selectedHero[P])
call DestroyEffect(selectedHeroGlow[P])
call DestroyEffect(selectedHeroGlow[P])
call BlzSetSpecialEffectPosition(backgroundHero[P], GARBAGE_DUMP_X, GARBAGE_DUMP_Y, 0)
call DeleteBackgroundHero(P)
elseif numPlayersWithHero == numSelectingPlayers and not isRepicking[P] then
call TimerStart( CreateTimer() , LAST_PLAYER_SELECT_END_DELAY , false , function EndHeroSelectionCaller )
endif
call NoCrash("OnPlayerLeave")
endfunction
//==========================================================================================================================================================
//Cycle Page.
//==========================================================================================================================================================
private function DisplayNewPage takes integer P returns nothing
local integer i
call BlzFrameSetVisible(heroSelectionButtonHighlight, PAGE_OF_CATEGORY[Hero.list[preselectedHeroIndex[P]].category] == currentPage)
set i = 1
loop
exitwhen i > NUMBER_OF_CATEGORIES
call BlzFrameSetVisible(heroSelectionCategory[i], PAGE_OF_CATEGORY[i] == currentPage)
set i = i + 1
endloop
set i = 1
loop
exitwhen i > NUMBER_OF_HEROES
call BlzFrameSetVisible(heroSelectionButton[i], PAGE_OF_CATEGORY[Hero.list[i].category] == currentPage and not Hero.list[i].unavailableToTeam[TEAM_OF_PLAYER[localPlayerId]])
set i = i + 1
endloop
endfunction
private function CyclePage takes nothing returns nothing
local player whichPlayer = GetTriggerPlayer()
local integer P = GetPlayerId(whichPlayer)
local integer i
call PlaySoundLocal( "Sound\\Interface\\BigButtonClick.flac" , localPlayerId == P )
if localPlayerId == P then
if BlzGetTriggerFrame() == heroSelectionButton[PAGE_DOWN] then
set currentPage = ModuloInteger(currentPage - 2, NUMBER_OF_PAGES) + 1
else
set currentPage = ModuloInteger(currentPage, NUMBER_OF_PAGES) + 1
endif
call DisplayNewPage(P)
endif
endfunction
//==========================================================================================================================================================
//Ban Hero.
//==========================================================================================================================================================
private function ExecuteBan takes Hero whichHero, boolean disable, integer whichPlayer returns nothing
local integer i
local integer P
if disable then
if whichHero.tooltipAbility != 0 then
call BlzFrameSetText(heroSelectionButtonTooltipText[whichHero.index] , BlzGetAbilityExtendedTooltip(whichHero.tooltipAbility, 0) + "|n|n|cffff0000This hero was banned.|r")
else
call BlzFrameSetText(heroSelectionButtonTooltipText[whichHero.index] , whichHero.tooltip + "|n|n|cffff0000This hero was banned.|r")
endif
else
if whichHero.tooltipAbility != 0 then
call BlzFrameSetText(heroSelectionButtonTooltipText[whichHero.index] , BlzGetAbilityExtendedTooltip(whichHero.tooltipAbility, 0))
else
call BlzFrameSetText(heroSelectionButtonTooltipText[whichHero.index] , whichHero.tooltip)
endif
endif
call BlzFrameSetSize(heroSelectionButtonTooltipText[whichHero.index] , TOOLTIP_WIDTH - 0.01 , 0.0 )
call BlzFrameSetSize(heroSelectionButtonTooltip[whichHero.index] , TOOLTIP_WIDTH , BlzFrameGetHeight(heroSelectionButtonTooltipText[whichHero.index]) + TOOLTIP_BASE_HEIGHT)
set i = 1
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
if P == whichPlayer or whichPlayer == -1 then
set heroIndexWasBanned[whichHero.index][P] = true
if localPlayerId == P then
call SetButtonTextures(whichHero.index, not HeroCanBePicked(whichHero.index, P))
if preselectedHeroIndex[P] == whichHero then
call BlzFrameSetEnable( heroAcceptButton , false )
call BlzFrameSetEnable( heroBanButton , false )
static if PHANTOM_HERO_WHEN_CANNOT_BE_PICKED then
call BlzSetSpecialEffectAlpha(selectedHero[P], 128)
call BlzSetSpecialEffectColor(selectedHero[P], 0, 0, 0)
endif
endif
endif
endif
set i = i + 1
endloop
endfunction
private function BanHero takes nothing returns nothing
local string message
local player whichPlayer = GetTriggerPlayer()
local integer P = GetPlayerId(whichPlayer)
local integer heroIndex = preselectedHeroIndex[P]
call InitCrashCheck("BanHero")
if localPlayerId == P then
call BlzFrameSetEnable(heroBanButton, false)
endif
set playerHasBan[P] = false
set message = GetPickedHeroDisplayedName(heroIndex, true) + " was banned."
static if LIBRARY_NeatMessages then
call NeatMessage(message)
else
call DisplayTextToPlayer(GetLocalPlayer(), TEXT_MESSAGE_X_OFFSET, TEXT_MESSAGE_Y_OFFSET, message)
endif
if OTHER_PLAYER_HERO_PICK_SOUND != null then
call PlaySoundLocal(OTHER_PLAYER_HERO_PICK_SOUND, true)
endif
call ExecuteBan(Hero.list[heroIndex], true, -1)
call NoCrash("BanHero")
endfunction
//==========================================================================================================================================================
//Pick Hero.
//==========================================================================================================================================================
private function PickHero takes nothing returns nothing
local integer i
local integer P
local integer Q
local string message
local player whichPlayer
local integer heroIndex
local timer t
local string modelPath
local boolean wasRandomSelect
local boolean concealed
local integer id
local boolean allHumanPlayersHaveHeroes
local effect pickEffect
local noArgCallback onLast = HeroSelectionOnLast
local onPickCallback onPick = HeroSelectionOnPick
call InitCrashCheck("PickHero")
if not isForcedSelect then
set whichPlayer = GetTriggerPlayer()
set P = GetPlayerId(whichPlayer)
set heroIndex = preselectedHeroIndex[P]
else
set P = storePlayerIndex
set whichPlayer = Player(P)
set heroIndex = storeHeroIndex
endif
set concealed = CONCEAL_HERO_PICKS_FROM_ENEMIES and localPlayerId != P and (TEAM_OF_PLAYER[localPlayerId] != TEAM_OF_PLAYER[P] or TEAM_OF_PLAYER[P] == 0)
//Random
if heroIndex == RANDOM_HERO then
set heroIndex = GetRandomHero(0, P)
if PICK_SOUND != null then
call PlaySoundLocal(PICK_SOUND , localPlayerId == P )
endif
set wasRandomSelect = true
static if CREATE_FOREGROUND_HERO then
if localPlayerId == P then
set modelPath = Hero.list[heroIndex].modelPath
else
set modelPath = ""
endif
call DestroyEffect(selectedHero[P])
call BlzSetSpecialEffectPosition( selectedHero[P] , GARBAGE_DUMP_X , GARBAGE_DUMP_Y , 0 )
set selectedHero[P] = AddSpecialEffect( modelPath , localForegroundHeroX , localForegroundHeroY )
call BlzSetSpecialEffectYaw( selectedHero[P] , Deg2Rad(localHeroSelectionAngle) + bj_PI )
call BlzSetSpecialEffectScale( selectedHero[P] , Hero.list[heroIndex].scalingValue )
endif
else
call PlaySoundLocal( "Sound\\Interface\\BigButtonClick.flac" , localPlayerId == P )
if PICK_SOUND != null then
call PlaySoundLocal(PICK_SOUND , localPlayerId == P )
endif
set wasRandomSelect = false
endif
//Disable Buttons for selecting player.
set i = 1
loop
exitwhen i > SUGGEST_RANDOM
if localPlayerId == P then
call SetButtonTextures(i, true)
endif
set i = i + 1
endloop
if localPlayerId == P then
call BlzFrameSetEnable( heroAcceptButton , false )
call BlzFrameSetEnable( heroBanButton , false )
endif
//Disable button for other players that have pre-selected that hero.
static if CREATE_FOREGROUND_HERO then
static if not HERO_CAN_BE_PICKED_MULTIPLE_TIMES then
call SetButtonTextures(heroIndex, true)
set i = 1
loop
exitwhen i > numSelectingPlayers
if localPlayerId == playerNumberOfSlot[i] and preselectedHeroIndex[playerNumberOfSlot[i]] == heroIndex then
call BlzFrameSetEnable( heroAcceptButton , false )
call BlzFrameSetEnable( heroBanButton , false )
static if PHANTOM_HERO_WHEN_CANNOT_BE_PICKED then
if playerNumberOfSlot[i] != P then
call BlzSetSpecialEffectAlpha(selectedHero[playerNumberOfSlot[i]], 128)
call BlzSetSpecialEffectColor(selectedHero[playerNumberOfSlot[i]], 0, 0, 0)
endif
endif
endif
set i = i + 1
endloop
endif
endif
//Set variables
set heroIndexWasPicked[heroIndex] = true
set pickedHeroIndex[P] = heroIndex
set heroIdOfPlayer[P] = Hero.list[heroIndex].unitId
set heroIconPathOfPlayer[P] = "ReplaceableTextures\\CommandButtons\\" + Hero.list[heroIndex].iconPath
set playerHasHero[P] = true
set playerHasBan[P] = false
set numPlayersWithHero = numPlayersWithHero + 1
//Text messages
if OTHER_PLAYER_HERO_PICK_SOUND != null then
call PlaySoundLocal(OTHER_PLAYER_HERO_PICK_SOUND, localPlayerId != P and (MESSAGE_EVEN_WHEN_CONCEALED or not concealed))
endif
set i = 1
loop
exitwhen i > numSelectingPlayers
set Q = playerNumberOfSlot[i]
if P != Q then
if CREATE_TEXT_MESSAGE_ON_PICK then
set message = ""
if (CONCEAL_HERO_PICKS_FROM_ENEMIES and (TEAM_OF_PLAYER[P] != TEAM_OF_PLAYER[Q] or TEAM_OF_PLAYER[P] == 0)) then
if MESSAGE_EVEN_WHEN_CONCEALED then
set message = message + coloredPlayerName[P] + " has selected a hero."
endif
else
if wasRandomSelect then
set message = message + coloredPlayerName[P] + " has randomly selected " + GetPickedHeroDisplayedName(heroIndex, false) + "."
else
set message = message + coloredPlayerName[P] + " has selected " + GetPickedHeroDisplayedName(heroIndex, false) + "."
endif
endif
static if INCLUDE_PROGRESSION_IN_MESSAGE then
set message = message + " " + I2S(numPlayersWithHero) + "/" + I2S(numSelectingPlayers) + " players have selected."
endif
if message != "" then
static if LIBRARY_NeatMessages then
call NeatMessageToPlayer(Player(Q), message)
else
call DisplayTextToPlayer(Player(Q), TEXT_MESSAGE_X_OFFSET, TEXT_MESSAGE_Y_OFFSET, message)
endif
endif
endif
else
if wasRandomSelect then
set message = "You randomly selected " + GetPickedHeroDisplayedName(heroIndex, false) + "."
else
set message = "You selected " + GetPickedHeroDisplayedName(heroIndex, false) + "."
endif
static if INCLUDE_PROGRESSION_IN_MESSAGE then
set message = message + " " + I2S(numPlayersWithHero) + "/" + I2S(numSelectingPlayers) + " players have selected."
endif
static if LIBRARY_NeatMessages then
call NeatMessageToPlayer(Player(P), message)
else
call DisplayTextToPlayer(Player(P), TEXT_MESSAGE_X_OFFSET, TEXT_MESSAGE_Y_OFFSET, message)
endif
endif
set i = i + 1
endloop
//Foreground hero
static if CREATE_FOREGROUND_HERO then
if PICK_EFFECT != null then
if localPlayerId == P then
set modelPath = PICK_EFFECT
else
set modelPath = ""
endif
endif
set pickEffect = AddSpecialEffect( modelPath , localForegroundHeroX , localForegroundHeroY )
call BlzSetSpecialEffectZ(pickEffect, GetLocZ(localForegroundHeroX, localForegroundHeroY) + FOREGROUND_HERO_Z)
call DestroyEffect(pickEffect)
set pickEffect = null
static if PLAY_ANIMATION_ON_PICK then
call PlayHeroAnimation(P, heroIndex, false)
endif
static if PLAY_EMOTE_ON_PICK then
call PlaySoundLocal( Hero.list[heroIndex].selectEmote , localPlayerId == P )
endif
if CREATE_BACKGROUND_HEROES or ESCAPE_PLAYER_AFTER_SELECTING or isRepicking[P] then
set t = CreateTimer()
call TimerStart( t , FOREGROUND_HERO_FADEOUT_DELAY , false , function FadeoutForegroundHero )
call SaveInteger( hash , GetHandleId(t) , 0 , 0 )
call SaveInteger( hash , GetHandleId(t) , 1 , P )
endif
endif
//Create Background Hero
static if CREATE_BACKGROUND_HEROES then
if concealed then
set modelPath = CONCEALED_HERO_EFFECT
else
set modelPath = Hero.list[heroIndex].modelPath
endif
set backgroundHero[P] = AddSpecialEffect( modelPath , BACKGROUND_HERO_X[P] , BACKGROUND_HERO_Y[P] )
call BlzSetSpecialEffectScale( backgroundHero[P] , Hero.list[heroIndex].scalingValue )
call BlzSetSpecialEffectAlpha( backgroundHero[P] , 0 )
call BlzSetSpecialEffectYaw( backgroundHero[P] , Atan2(localForegroundHeroY - BACKGROUND_HERO_Y[P] + Sin(Deg2Rad(localHeroSelectionAngle))*BACKGROUND_HERO_FACING_POINT_OFFSET , localForegroundHeroX - BACKGROUND_HERO_X[P] + Cos(Deg2Rad(localHeroSelectionAngle))*BACKGROUND_HERO_FACING_POINT_OFFSET) )
call BlzSetSpecialEffectColorByPlayer( backgroundHero[P] , whichPlayer )
call BlzSetSpecialEffectColor( backgroundHero[P] , Hero.list[heroIndex].red , Hero.list[heroIndex].green , Hero.list[heroIndex].blue)
if Hero.list[heroIndex].needsHeroGlow then
set backgroundHeroGlow[P] = AddSpecialEffect( "GeneralHeroGlow.mdx" , BACKGROUND_HERO_X[P] , BACKGROUND_HERO_Y[P] )
call BlzSetSpecialEffectAlpha( backgroundHeroGlow[P], 0 )
call BlzSetSpecialEffectColorByPlayer( backgroundHeroGlow[P] , whichPlayer )
endif
if concealed then
call BlzSetSpecialEffectColor(backgroundHero[P], 0, 0, 0)
endif
static if PLAY_ANIMATION_ON_BACKGROUND_HERO then
call PlayHeroAnimation(P, heroIndex, true)
endif
if BACKGROUND_HERO_FADEIN_EFFECT != null then
if localPlayerId != P then
set modelPath = BACKGROUND_HERO_FADEIN_EFFECT
else
set modelPath = ""
endif
call DestroyEffect(AddSpecialEffect(modelPath, BACKGROUND_HERO_X[P], BACKGROUND_HERO_Y[P] ))
endif
static if PLAY_EMOTE_ON_BACKGROUND_HERO then
call PlaySoundLocal(Hero.list[heroIndex].selectEmote, localPlayerId != P and isInHeroSelection[localPlayerId] and not concealed)
endif
static if CREATE_SHADOWS then
if localPlayerId == P then
set id = NO_SHADOW_DESTRUCTABLE_ID
else
set id = SHADOW_DESTRUCTABLE_ID
endif
set backgroundHeroShadow[P] = CreateDestructable(id, BACKGROUND_HERO_X[P], BACKGROUND_HERO_Y[P], 0, 1, 0)
endif
if BACKGROUND_HERO_FADEIN_TIME > 0 then
set t = CreateTimer()
call TimerStart( t , BACKGROUND_HERO_FADEIN_TIME/50.0 , true , function FadeInBackgroundHero )
set id = GetHandleId(t)
call SaveInteger( hash , id , 0 , 0 )
call SaveInteger( hash , id , 1 , P )
call SaveBoolean( hash , id , 2 , concealed)
else
if concealed then
call BlzSetSpecialEffectAlpha( backgroundHero[P] , 128 )
else
call BlzSetSpecialEffectAlpha( backgroundHero[P] , 255 )
endif
endif
endif
if ESCAPE_PLAYER_AFTER_SELECTING or isRepicking[P] then
set t = CreateTimer()
call TimerStart( t , PLAYER_PICK_ESCAPE_DELAY , false , function EscapePlayerCaller)
call SaveInteger(hash, GetHandleId(t), 0, P)
endif
//End hero selection.
if not isRepicking[P] and numPlayersWithHero == numSelectingPlayers then
call onLast.evaluate()
if TIME_LIMIT > 0 then
call PauseTimer(countdownTimer)
endif
static if not ESCAPE_PLAYER_AFTER_SELECTING then
call TimerStart( CreateTimer() , LAST_PLAYER_SELECT_END_DELAY , false , function EndHeroSelectionCaller )
endif
endif
static if COMPUTER_AUTO_PICK_RANDOM_HERO then
if playerIsHuman[P] and not isRepicking[P] then
set allHumanPlayersHaveHeroes = true
set i = 1
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
if playerIsHuman[P] and not playerHasHero[P] then
set allHumanPlayersHaveHeroes = false
exitwhen true
endif
set i = i + 1
endloop
if allHumanPlayersHaveHeroes then
set i = 1
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
if not playerIsHuman[P] and not playerHasHero[P] and isInHeroSelection[P] then
set isForcedSelect = true
set storePlayerIndex = P
set storeHeroIndex = RANDOM_HERO
call PickHero()
set isForcedSelect = false
endif
set i = i + 1
endloop
endif
endif
endif
call onPick.evaluate(whichPlayer, Hero.list[heroIndex], wasRandomSelect, isRepicking[P])
call NoCrash("PickHero")
set t = null
endfunction
//==========================================================================================================================================================
//Preselect Hero.
//==========================================================================================================================================================
private function PreselectRandomCycle takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer P = LoadInteger(hash , GetHandleId(t) , 0 )
local integer heroIndexShown = LoadInteger( hash , GetHandleId(t) , 1 )
local integer newHeroShown
call InitCrashCheck("PreselectRandomCycle")
if preselectedHeroIndex[P] == RANDOM_HERO and not playerHasHero[P] then
set newHeroShown = GetRandomHero(heroIndexShown, P)
call CreateNewForegroundHero(P, newHeroShown)
call SaveInteger( hash , GetHandleId(t) , 1 , newHeroShown )
else
call FlushChildHashtable( hash , GetHandleId(t) )
call DestroyTimer(t)
endif
call NoCrash("PreselectRandomCycle")
set t = null
endfunction
private function PreselectHero takes nothing returns nothing
local player whichPlayer = GetTriggerPlayer()
local integer P = GetPlayerId(whichPlayer)
local integer i
local framehandle whichFrame = BlzGetTriggerFrame()
local boolean isSuggest
local boolean isRandom
local integer heroIndex
local string modelPath = ""
local timer t
local integer oldHero = preselectedHeroIndex[P]
local integer id
local effect preselectEffect
local playerHeroHeroCallback onPreselect = HeroSelectionOnPreselect
local integer firstRandomHero
if localPlayerId == P then
call BlzFrameSetEnable(whichFrame, false)
call BlzFrameSetEnable(whichFrame, true)
endif
if heroPreselectionDisabledForPlayer[P] then
return
endif
call InitCrashCheck("PreselectHero")
call PlaySoundLocal( "Sound\\Interface\\BigButtonClick.flac" , localPlayerId == P )
if PRESELECT_EFFECT != null then
if localPlayerId == P then
set modelPath = PRESELECT_EFFECT
endif
set preselectEffect = AddSpecialEffect( modelPath , localForegroundHeroX , localForegroundHeroY )
call BlzSetSpecialEffectZ(preselectEffect, GetLocZ(localForegroundHeroX, localForegroundHeroY) + FOREGROUND_HERO_Z)
call DestroyEffect(preselectEffect)
set preselectEffect = null
endif
set i = 1
loop
exitwhen i > SUGGEST_RANDOM
if whichFrame == heroSelectionButton[i] then
set heroIndex = i
if heroIndex == RANDOM_HERO then
set isSuggest = false
set isRandom = true
elseif heroIndex == SUGGEST_RANDOM then
set isSuggest = true
set isRandom = false
else
set isSuggest = false
set isRandom = false
endif
exitwhen true
endif
set i = i + 1
endloop
set whichFrame = null
if isSuggest then
set heroIndex = GetRandomHero(preselectedHeroIndex[P], P)
if localPlayerId == P and PAGE_OF_CATEGORY[Hero.list[heroIndex].category] != currentPage then
set currentPage = PAGE_OF_CATEGORY[Hero.list[heroIndex].category]
call DisplayNewPage(P)
endif
endif
if localPlayerId == P then
if not isRandom then
set i = 1
loop
exitwhen i > NUMBER_OF_ABILITY_FRAMES
if Hero.list[heroIndex].abilities[i] != 0 then
call BlzFrameSetTexture( heroSelectionAbility[i] , BlzGetAbilityIcon( Hero.list[heroIndex].abilities[i] ) , 0, true )
call BlzFrameSetText( heroSelectionAbilityTooltipTitle[i] , GetHeroAbilityName(Hero.list[heroIndex].abilities[i]) )
if Hero.list[heroIndex].isNonHeroAbility[i] then
call BlzFrameSetText( heroSelectionAbilityTooltipText[i] , BlzGetAbilityExtendedTooltip( Hero.list[heroIndex].abilities[i] , 0 ) )
else
call BlzFrameSetText( heroSelectionAbilityTooltipText[i] , BlzGetAbilityResearchExtendedTooltip( Hero.list[heroIndex].abilities[i] , 0 ) )
endif
call BlzFrameSetSize(heroSelectionAbilityTooltipText[i] , TOOLTIP_WIDTH - 0.01 , 0.0 )
call BlzFrameSetSize(heroSelectionAbilityTooltip[i] , TOOLTIP_WIDTH , BlzFrameGetHeight(heroSelectionAbilityTooltipText[i]) + TOOLTIP_BASE_HEIGHT)
call BlzFrameSetVisible( heroSelectionAbility[i] , true )
else
call BlzFrameSetVisible( heroSelectionAbility[i] , false )
endif
set i = i + 1
endloop
else
set i = 1
loop
exitwhen i > NUMBER_OF_ABILITY_FRAMES
call BlzFrameSetVisible( heroSelectionAbility[i] , false )
set i = i + 1
endloop
endif
call BlzFrameSetEnable( heroAcceptButton , HeroCanBePicked(heroIndex, P) and not playerHasHero[P] and not heroSelectionDisabledForPlayer[P] )
call BlzFrameSetEnable( heroBanButton , HeroCanBePicked(heroIndex, P) and heroIndex != RANDOM_HERO and playerHasBan[P] )
endif
static if CREATE_FOREGROUND_HERO then
if not playerHasHero[P] and preselectedHeroIndex[P] != heroIndex then
if not isRandom then
call CreateNewForegroundHero(P, heroIndex)
if HeroCanBePicked(heroIndex, P) and not heroSelectionDisabledForPlayer[P] then
static if PLAY_EMOTE_ON_PRESELECT then
call PlaySoundLocal( Hero.list[heroIndex].selectEmote , localPlayerId == P )
endif
static if PLAY_ANIMATION_ON_PRESELECT then
call PlayHeroAnimation(P, heroIndex, false)
endif
endif
elseif preselectedHeroIndex[P] != RANDOM_HERO then
static if RANDOM_SELECT_CYCLE_STYLE then
set firstRandomHero = GetRandomHero(0,P)
call CreateNewForegroundHero(P, firstRandomHero)
set t = CreateTimer()
call TimerStart( t , RANDOM_SELECT_CYCLE_INTERVAL , true , function PreselectRandomCycle )
call SaveInteger( hash , GetHandleId(t) , 0 , P )
call SaveInteger( hash , GetHandleId(t) , 1 , firstRandomHero )
set t = null
else
call CreateNewForegroundHero(P, RANDOM_HERO)
endif
endif
endif
static if CREATE_SHADOWS then
call RemoveDestructable(foregroundHeroShadow)
if preselectedHeroIndex[localPlayerId] == 0 or playerHasHero[localPlayerId] then
set id = NO_SHADOW_DESTRUCTABLE_ID
else
set id = SHADOW_DESTRUCTABLE_ID
endif
set foregroundHeroShadow = CreateDestructable(id, localForegroundHeroX, localForegroundHeroY, 0, 1, 0)
endif
endif
if localPlayerId == P then
if not isRandom then
call BlzFrameSetPoint( heroSelectionButtonHighlight , FRAMEPOINT_TOPLEFT , heroSelectionButton[heroIndex] , FRAMEPOINT_TOPLEFT , 0.005*0.039/MENU_BUTTON_SIZE , -0.005*0.039/MENU_BUTTON_SIZE )
call BlzFrameSetPoint( heroSelectionButtonHighlight , FRAMEPOINT_BOTTOMRIGHT , heroSelectionButton[heroIndex] , FRAMEPOINT_BOTTOMRIGHT , -0.005*0.039/MENU_BUTTON_SIZE , 0.005*0.039/MENU_BUTTON_SIZE )
else
call BlzFrameSetPoint( heroSelectionButtonHighlight , FRAMEPOINT_TOPLEFT , heroSelectionButton[RANDOM_HERO] , FRAMEPOINT_TOPLEFT , 0.005*0.039/MENU_BUTTON_SIZE , -0.005*0.039/MENU_BUTTON_SIZE )
call BlzFrameSetPoint( heroSelectionButtonHighlight , FRAMEPOINT_BOTTOMRIGHT , heroSelectionButton[RANDOM_HERO] , FRAMEPOINT_BOTTOMRIGHT , -0.005*0.039/MENU_BUTTON_SIZE , 0.005*0.039/MENU_BUTTON_SIZE )
endif
call BlzFrameSetVisible( heroSelectionButtonHighlight , true )
endif
set preselectedHeroIndex[P] = heroIndex
call onPreselect.evaluate(whichPlayer, Hero.list[oldHero], Hero.list[heroIndex])
call NoCrash("PreselectHero")
endfunction
//==========================================================================================================================================================
//Init.
//==========================================================================================================================================================
private function InitMenu takes nothing returns nothing
local integer i
local integer j
local integer jLocal
local integer h
local integer k
local trigger trig
local real buttonSpacing = MENU_BUTTON_SIZE + MENU_BUTTON_BUTTON_GAP
local real menuWidth
local real menuHeight
local real Ystart
local real currentY
local real currentYLowest
local real widthDiff
local integer buttonsInRandomRow
local integer column
local integer heroesThisCategory
local integer array whichHeroes
local integer heroesThisCategoryLocal
local boolean newRow
local real xOffset
local real glueTextOffset = 0.005
local boolean hasNoCategoryHeroes = false
local boolean firstCategoryThisPage
local real pageCycleDownX
local real pageCycleUpX
local real pageCycleDownY
local real pageCycleUpY
local boolean pageCycleTextType
local real pageCycleButtonSpacing
local real pageCycleButtonSize
local real pageCycleScaleOffset
local real wideScreenAreaWidth
local real menuXLeftLocal
local real categoryScale
local integer localClientHeight = BlzGetLocalClientHeight()
call InitCrashCheck("InitMenu")
call BlzLoadTOCFile("HeroSelectionTemplates.toc")
if localClientHeight > 0 then
set wideScreenAreaWidth = 0.6*(BlzGetLocalClientWidth()/I2R(localClientHeight) - 4.0/3.0)/2.0
else
set wideScreenAreaWidth = 0.6*(16.0/9.0 - 4.0/3.0)/2.0
endif
set menuXLeftLocal = RMaxBJ(0, MENU_X_LEFT + wideScreenAreaWidth)
set tooltipLeftXLocal = RMinBJ(TOOLTIP_LEFT_X + wideScreenAreaWidth, 0.8 + wideScreenAreaWidth - TOOLTIP_WIDTH)
set menuWidth = 2*MENU_LEFT_RIGHT_EDGE_GAP + MENU_NUMBER_OF_COLUMNS*MENU_BUTTON_SIZE + (MENU_NUMBER_OF_COLUMNS - 1)*MENU_BUTTON_BUTTON_GAP
if MENU_BORDER_TILE_SIZE > 0 then
set widthDiff = menuWidth
set menuWidth = R2I(menuWidth/MENU_BORDER_TILE_SIZE + 0.99)*MENU_BORDER_TILE_SIZE
set widthDiff = menuWidth - widthDiff
else
set widthDiff = 0
endif
set heroSelectionMenu = BlzCreateFrame("HeroSelectionMenu", fullScreenParent, 0, 0)
call BlzFrameSetPoint(heroSelectionMenu, FRAMEPOINT_TOPLEFT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal , MENU_Y_TOP )
call BlzFrameSetPoint(heroSelectionMenu, FRAMEPOINT_BOTTOMRIGHT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal + menuWidth , 0 )
set heroSelectionButtonTrigger = CreateTrigger()
call TriggerAddAction( heroSelectionButtonTrigger , function PreselectHero )
set Ystart = MENU_Y_TOP - MENU_TOP_EDGE_GAP
set currentYLowest = Ystart
//Hero buttons
set k = 1
loop
exitwhen k > NUMBER_OF_PAGES
set currentY = Ystart
set firstCategoryThisPage = true
set i = 0
loop
exitwhen i > NUMBER_OF_CATEGORIES
if PAGE_OF_CATEGORY[i] == k or (PAGE_OF_CATEGORY[i] == 0 and k == 1) then
if i > 0 then
if not firstCategoryThisPage or CATEGORY_NAMES[i] != null then
set currentY = currentY - MENU_CATEGORY_GAP
endif
if not firstCategoryThisPage then
set currentY = currentY - buttonSpacing
endif
if CATEGORY_NAMES[i] != null then
set categoryScale = MENU_CATEGORY_FONT_SIZE/10.
set heroSelectionCategory[i] = BlzCreateFrameByType("TEXT", CATEGORY_NAMES[i], heroSelectionMenu, "", 0)
call BlzFrameSetPoint(heroSelectionCategory[i] , FRAMEPOINT_TOPLEFT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal/categoryScale , (currentY + MENU_CATEGORY_GAP/2 + MENU_CATEGORY_TITLE_Y + 0.02)/categoryScale)
call BlzFrameSetPoint(heroSelectionCategory[i] , FRAMEPOINT_BOTTOMRIGHT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , (menuXLeftLocal + menuWidth)/categoryScale , (currentY + MENU_CATEGORY_GAP/2 + MENU_CATEGORY_TITLE_Y - 0.02)/categoryScale)
call BlzFrameSetTextAlignment(heroSelectionCategory[i] , TEXT_JUSTIFY_MIDDLE , TEXT_JUSTIFY_CENTER)
call BlzFrameSetScale(heroSelectionCategory[i], categoryScale)
call BlzFrameSetText(heroSelectionCategory[i] , CATEGORY_NAMES[i])
if k != 1 then
call BlzFrameSetVisible(heroSelectionCategory[i], false)
endif
endif
endif
set heroesThisCategory = 0
set heroesThisCategoryLocal = 0
set j = 1
loop
exitwhen j > NUMBER_OF_HEROES
if Hero.list[j].category == i then
set heroesThisCategory = heroesThisCategory + 1
set whichHeroes[heroesThisCategory] = j
if not Hero.list[j].unavailableToTeam[TEAM_OF_PLAYER[localPlayerId]] then
set heroesThisCategoryLocal = heroesThisCategoryLocal + 1
endif
endif
set j = j + 1
endloop
if i == 0 and heroesThisCategoryLocal > 0 then
set hasNoCategoryHeroes = true
endif
set column = 0
set newRow = true
set j = 1
set jLocal = 1
loop
exitwhen j > heroesThisCategory
if newRow then
if heroesThisCategoryLocal - (jLocal-1) < MENU_NUMBER_OF_COLUMNS then
set xOffset = buttonSpacing/2 * (MENU_NUMBER_OF_COLUMNS - (heroesThisCategoryLocal - (jLocal-1))) + widthDiff/2
else
set xOffset = widthDiff/2
endif
if jLocal != 1 then
set currentY = currentY - buttonSpacing
endif
endif
set h = whichHeroes[j]
set heroSelectionButton[h] = BlzCreateFrameByType("GLUETEXTBUTTON", "heroSelectionButton_" + I2S(h), heroSelectionMenu, "ScriptDialogButton", 0)
call BlzFrameSetPoint( heroSelectionButton[h] , FRAMEPOINT_TOPLEFT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal + MENU_LEFT_RIGHT_EDGE_GAP + xOffset + column*buttonSpacing - glueTextOffset, currentY + glueTextOffset )
call BlzFrameSetPoint( heroSelectionButton[h] , FRAMEPOINT_BOTTOMRIGHT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal + MENU_LEFT_RIGHT_EDGE_GAP + xOffset + column*buttonSpacing + MENU_BUTTON_SIZE + glueTextOffset, currentY - MENU_BUTTON_SIZE - glueTextOffset )
call SetButtonFrames(h)
call SetButtonTextures(h, not PRE_SELECT_BEFORE_ENABLED or Hero.list[h].unavailable)
if BlzGetAbilityExtendedTooltip(Hero.list[h].tooltipAbility, 0) != null then
if not Hero.list[h].unavailable then
call SetButtonTooltip(h, Hero.list[h].name, BlzGetAbilityExtendedTooltip(Hero.list[h].tooltipAbility, 0))
else
call SetButtonTooltip(h, Hero.list[h].name, BlzGetAbilityExtendedTooltip(Hero.list[h].tooltipAbility, 0) + "|n|n|cffff0000Not available.|r")
endif
else
if not Hero.list[h].unavailable then
call SetButtonTooltip(h, Hero.list[h].name, Hero.list[h].tooltip)
else
call SetButtonTooltip(h, Hero.list[h].name, Hero.list[h].tooltip + "|n|n|cffff0000Not available.|r")
endif
endif
call BlzFrameSetVisible(heroSelectionButton[h], not Hero.list[h].unavailableToTeam[TEAM_OF_PLAYER[localPlayerId]] and k == 1)
call BlzTriggerRegisterFrameEvent( heroSelectionButtonTrigger, heroSelectionButton[h] , FRAMEEVENT_CONTROL_CLICK )
if not Hero.list[h].unavailableToTeam[TEAM_OF_PLAYER[localPlayerId]] then
set column = column + 1
if column == MENU_NUMBER_OF_COLUMNS and jLocal < heroesThisCategoryLocal then
set newRow = true
set column = 0
else
set newRow = false
endif
set jLocal = jLocal + 1
endif
set j = j + 1
endloop
if i > 0 or hasNoCategoryHeroes then
set firstCategoryThisPage = false
endif
endif
set i = i + 1
endloop
if currentY < currentYLowest then
set currentYLowest = currentY
endif
set k = k + 1
endloop
set currentY = currentYLowest
//Random
if MENU_INCLUDE_RANDOM_PICK or MENU_INCLUDE_SUGGEST_RANDOM or (NUMBER_OF_PAGES > 1 and PAGE_CYCLE_BUTTON_STYLE == "EnvelopRandomButton") then
set currentY = currentY - buttonSpacing - MENU_HEROES_RANDOM_GAP
set buttonsInRandomRow = 0
static if MENU_INCLUDE_RANDOM_PICK then
set buttonsInRandomRow = buttonsInRandomRow + 1
endif
static if MENU_INCLUDE_SUGGEST_RANDOM then
set buttonsInRandomRow = buttonsInRandomRow + 1
endif
if (NUMBER_OF_PAGES > 1 and PAGE_CYCLE_BUTTON_STYLE == "EnvelopRandomButton") then
set buttonsInRandomRow = buttonsInRandomRow + 2
endif
if HERO_SELECTION_ENABLE_DEBUG_MODE and MENU_NUMBER_OF_COLUMNS < buttonsInRandomRow then
call BJDebugMsg("|cffff0000Warning:|r Not enough columns set to accomodate all buttons in the random row...")
endif
endif
static if MENU_INCLUDE_RANDOM_PICK then
if MENU_INCLUDE_SUGGEST_RANDOM then
set xOffset = buttonSpacing/2 * (MENU_NUMBER_OF_COLUMNS - 2) + widthDiff/2
else
set xOffset = buttonSpacing/2 * (MENU_NUMBER_OF_COLUMNS - 1) + widthDiff/2
endif
set heroSelectionButton[RANDOM_HERO] = BlzCreateFrameByType("GLUETEXTBUTTON", "heroRandomButton" , heroSelectionMenu, "ScriptDialogButton", 0)
call BlzFrameSetPoint( heroSelectionButton[RANDOM_HERO] , FRAMEPOINT_TOPLEFT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal + MENU_LEFT_RIGHT_EDGE_GAP + xOffset - glueTextOffset, currentY + glueTextOffset )
call BlzFrameSetPoint( heroSelectionButton[RANDOM_HERO] , FRAMEPOINT_BOTTOMRIGHT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal + MENU_LEFT_RIGHT_EDGE_GAP + xOffset + MENU_BUTTON_SIZE + glueTextOffset, currentY - MENU_BUTTON_SIZE - glueTextOffset )
call SetButtonFrames(RANDOM_HERO)
call SetButtonTextures(RANDOM_HERO, not PRE_SELECT_BEFORE_ENABLED)
call SetButtonTooltip(RANDOM_HERO, "Select Random", RANDOM_HERO_TOOLTIP)
call BlzTriggerRegisterFrameEvent( heroSelectionButtonTrigger, heroSelectionButton[RANDOM_HERO] , FRAMEEVENT_CONTROL_CLICK )
endif
//Suggest
static if MENU_INCLUDE_SUGGEST_RANDOM then
if MENU_INCLUDE_RANDOM_PICK then
set xOffset = buttonSpacing/2 * MENU_NUMBER_OF_COLUMNS + widthDiff/2
else
set xOffset = buttonSpacing/2 * (MENU_NUMBER_OF_COLUMNS - 1) + widthDiff/2
endif
set heroSelectionButton[SUGGEST_RANDOM] = BlzCreateFrameByType("GLUETEXTBUTTON", "heroRandomButton" , heroSelectionMenu, "ScriptDialogButton", 0)
call BlzFrameSetPoint( heroSelectionButton[SUGGEST_RANDOM] , FRAMEPOINT_TOPLEFT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal + MENU_LEFT_RIGHT_EDGE_GAP + xOffset - glueTextOffset, currentY + glueTextOffset )
call BlzFrameSetPoint( heroSelectionButton[SUGGEST_RANDOM] , FRAMEPOINT_BOTTOMRIGHT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal + MENU_LEFT_RIGHT_EDGE_GAP + xOffset + MENU_BUTTON_SIZE + glueTextOffset, currentY - MENU_BUTTON_SIZE - glueTextOffset )
call SetButtonFrames(SUGGEST_RANDOM)
call SetButtonTextures(SUGGEST_RANDOM, not PRE_SELECT_BEFORE_ENABLED)
call SetButtonTooltip(SUGGEST_RANDOM, "Suggest Random", SUGGEST_RANDOM_TOOLTIP)
call BlzTriggerRegisterFrameEvent( heroSelectionButtonTrigger, heroSelectionButton[SUGGEST_RANDOM] , FRAMEEVENT_CONTROL_CLICK )
endif
//Set Bottom Corners
set menuHeight = MENU_Y_TOP - (currentY - buttonSpacing - MENU_BOTTOM_EDGE_GAP)
if MENU_BORDER_TILE_SIZE > 0 then
set menuHeight = R2I(menuHeight/MENU_BORDER_TILE_SIZE + 0.99)*MENU_BORDER_TILE_SIZE
endif
call BlzFrameSetPoint(heroSelectionMenu, FRAMEPOINT_BOTTOMRIGHT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal + menuWidth , MENU_Y_TOP - menuHeight )
//Page Cycle Buttons
if NUMBER_OF_PAGES > 1 then
set pageCycleTrigger = CreateTrigger()
call TriggerAddAction(pageCycleTrigger, function CyclePage)
set pageCycleButtonSpacing = buttonSpacing * MENU_PAGE_CYCLE_SCALE
set pageCycleButtonSize = MENU_BUTTON_SIZE * MENU_PAGE_CYCLE_SCALE
set pageCycleScaleOffset = (buttonSpacing - pageCycleButtonSpacing)/2
if PAGE_CYCLE_BUTTON_STYLE == "EnvelopRandomButton" then
set pageCycleDownX = menuXLeftLocal + MENU_LEFT_RIGHT_EDGE_GAP + buttonSpacing/2*(MENU_NUMBER_OF_COLUMNS - buttonsInRandomRow) + widthDiff/2
set pageCycleUpX = menuXLeftLocal + MENU_LEFT_RIGHT_EDGE_GAP + buttonSpacing/2*(MENU_NUMBER_OF_COLUMNS + buttonsInRandomRow - 2) + widthDiff/2
set pageCycleDownY = currentY
set pageCycleUpY = currentY
set pageCycleTextType = false
elseif PAGE_CYCLE_BUTTON_STYLE == "LeftRightMenuButton" then
set pageCycleDownX = menuXLeftLocal + MENU_LEFT_RIGHT_EDGE_GAP + widthDiff/2
set pageCycleUpX = menuXLeftLocal + MENU_LEFT_RIGHT_EDGE_GAP + pageCycleButtonSpacing*(MENU_NUMBER_OF_COLUMNS - 1) + widthDiff/2
set pageCycleDownY = currentY
set pageCycleUpY = currentY
set pageCycleTextType = false
elseif PAGE_CYCLE_BUTTON_STYLE == "BelowRandomButton" then
set pageCycleDownX = MENU_LEFT_RIGHT_EDGE_GAP + buttonSpacing/2*(MENU_NUMBER_OF_COLUMNS - 2) + widthDiff/2
set pageCycleUpX = MENU_LEFT_RIGHT_EDGE_GAP + buttonSpacing/2*MENU_NUMBER_OF_COLUMNS + widthDiff/2
set currentY = currentY - pageCycleButtonSpacing - MENU_HEROES_RANDOM_GAP
set pageCycleDownY = currentY
set pageCycleUpY = currentY
set pageCycleTextType = false
set menuHeight = MENU_Y_TOP - (currentY - buttonSpacing - MENU_BOTTOM_EDGE_GAP)
if MENU_BORDER_TILE_SIZE > 0 then
set menuHeight = R2I(menuHeight/MENU_BORDER_TILE_SIZE + 0.99)*MENU_BORDER_TILE_SIZE
endif
call BlzFrameSetPoint(heroSelectionMenu, FRAMEPOINT_BOTTOMRIGHT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal + menuWidth , MENU_Y_TOP - menuHeight )
elseif PAGE_CYCLE_BUTTON_STYLE == "LeftRightMiddleButton" then
set pageCycleDownX = menuXLeftLocal - pageCycleButtonSize/2
set pageCycleUpX = menuXLeftLocal + menuWidth - pageCycleButtonSize/2
set pageCycleDownY = MENU_Y_TOP - (menuHeight - pageCycleButtonSize)/2
set pageCycleUpY = pageCycleDownY
set pageCycleTextType = false
elseif PAGE_CYCLE_BUTTON_STYLE == "RightVerticalButton" then
set pageCycleDownX = menuXLeftLocal + menuWidth - pageCycleButtonSize/2
set pageCycleUpX = menuXLeftLocal + menuWidth - pageCycleButtonSize/2
set pageCycleDownY = MENU_Y_TOP - menuHeight/2
set pageCycleUpY = MENU_Y_TOP - menuHeight/2 + pageCycleButtonSpacing
set pageCycleTextType = false
elseif PAGE_CYCLE_BUTTON_STYLE == "LeftVerticalButton" then
set pageCycleDownX = menuXLeftLocal - pageCycleButtonSize/2
set pageCycleUpX = menuXLeftLocal - pageCycleButtonSize/2
set pageCycleDownY = MENU_Y_TOP - menuHeight/2
set pageCycleUpY = MENU_Y_TOP - menuHeight/2 + pageCycleButtonSpacing
set pageCycleTextType = false
elseif PAGE_CYCLE_BUTTON_STYLE == "EnvelopAcceptButton" then
set pageCycleDownX = menuXLeftLocal + menuWidth/2 - MENU_PAGE_CYCLE_SCALE*SELECT_BUTTON_WIDTH - SELECT_BUTTON_WIDTH/2
set pageCycleUpX = menuXLeftLocal + menuWidth/2 + SELECT_BUTTON_WIDTH/2
set pageCycleDownY = MENU_Y_TOP - menuHeight
set pageCycleUpY = pageCycleDownY
set pageCycleTextType = true
elseif PAGE_CYCLE_BUTTON_STYLE == "LeftRightBottomButton" then
set pageCycleDownX = menuXLeftLocal
set pageCycleUpX = menuXLeftLocal + menuWidth - SELECT_BUTTON_WIDTH*MENU_PAGE_CYCLE_SCALE
set pageCycleDownY = MENU_Y_TOP - menuHeight
set pageCycleUpY = pageCycleDownY
set pageCycleTextType = true
elseif PAGE_CYCLE_BUTTON_STYLE == "TangentTopButton" then
set pageCycleDownX = menuXLeftLocal + menuWidth/2 - SELECT_BUTTON_WIDTH*MENU_PAGE_CYCLE_SCALE
set pageCycleUpX = menuXLeftLocal + menuWidth/2
set pageCycleDownY = MENU_Y_TOP - 0.0175
set pageCycleUpY = pageCycleDownY
set pageCycleTextType = true
elseif PAGE_CYCLE_BUTTON_STYLE == "LeftRightTopButton" then
set pageCycleDownX = menuXLeftLocal
set pageCycleUpX = menuXLeftLocal + menuWidth - SELECT_BUTTON_WIDTH*MENU_PAGE_CYCLE_SCALE
set pageCycleDownY = MENU_Y_TOP - 0.0175
set pageCycleUpY = pageCycleDownY
set pageCycleTextType = true
elseif HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Unrecognized page cycle button style (" + PAGE_CYCLE_BUTTON_STYLE + ").")
endif
if pageCycleTextType then
set heroSelectionButton[PAGE_DOWN] = BlzCreateFrameByType("GLUETEXTBUTTON", "pageDownButton" , heroSelectionMenu, "ScriptDialogButton", 0)
call BlzFrameSetPoint( heroSelectionButton[PAGE_DOWN] , FRAMEPOINT_TOPLEFT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , pageCycleDownX + MENU_PAGE_CYCLE_X_OFFSET , pageCycleDownY + 0.012 + 0.012*SELECT_BUTTON_SCALE*MENU_PAGE_CYCLE_SCALE + MENU_PAGE_CYCLE_Y_OFFSET )
call BlzFrameSetPoint( heroSelectionButton[PAGE_DOWN] , FRAMEPOINT_BOTTOMRIGHT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , pageCycleDownX + MENU_PAGE_CYCLE_X_OFFSET + SELECT_BUTTON_WIDTH*MENU_PAGE_CYCLE_SCALE , pageCycleDownY - 0.003 - 0.003*SELECT_BUTTON_SCALE*MENU_PAGE_CYCLE_SCALE + MENU_PAGE_CYCLE_Y_OFFSET )
call BlzFrameSetText( heroSelectionButton[PAGE_DOWN] , "Prev" )
call BlzFrameSetScale( heroSelectionButton[PAGE_DOWN] , SELECT_BUTTON_SCALE*MENU_PAGE_CYCLE_SCALE )
else
set heroSelectionButton[PAGE_DOWN] = BlzCreateFrameByType("GLUETEXTBUTTON", "pageDownButton" , heroSelectionMenu, "ScriptDialogButton", 0)
call BlzFrameSetPoint( heroSelectionButton[PAGE_DOWN] , FRAMEPOINT_TOPLEFT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , pageCycleDownX + MENU_PAGE_CYCLE_X_OFFSET - glueTextOffset + pageCycleScaleOffset, pageCycleDownY + glueTextOffset + MENU_PAGE_CYCLE_Y_OFFSET - pageCycleScaleOffset )
call BlzFrameSetPoint( heroSelectionButton[PAGE_DOWN] , FRAMEPOINT_BOTTOMRIGHT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , pageCycleDownX + MENU_PAGE_CYCLE_X_OFFSET + pageCycleButtonSize + glueTextOffset + pageCycleScaleOffset, pageCycleDownY - pageCycleButtonSize - glueTextOffset + MENU_PAGE_CYCLE_Y_OFFSET - pageCycleScaleOffset )
call SetButtonFrames(PAGE_DOWN)
call SetButtonTextures(PAGE_DOWN, not PRE_SELECT_BEFORE_ENABLED)
call SetButtonTooltip(PAGE_DOWN, "Page Down", "Go to the previous page.")
endif
call BlzTriggerRegisterFrameEvent( pageCycleTrigger, heroSelectionButton[PAGE_DOWN] , FRAMEEVENT_CONTROL_CLICK )
if pageCycleTextType then
set heroSelectionButton[PAGE_UP] = BlzCreateFrameByType("GLUETEXTBUTTON", "pageUpButton" , heroSelectionMenu, "ScriptDialogButton", 0)
call BlzFrameSetPoint( heroSelectionButton[PAGE_UP] , FRAMEPOINT_TOPLEFT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , pageCycleUpX - MENU_PAGE_CYCLE_X_OFFSET , pageCycleUpY + 0.012 + 0.012*SELECT_BUTTON_SCALE*MENU_PAGE_CYCLE_SCALE + MENU_PAGE_CYCLE_Y_OFFSET )
call BlzFrameSetPoint( heroSelectionButton[PAGE_UP] , FRAMEPOINT_BOTTOMRIGHT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , pageCycleUpX - MENU_PAGE_CYCLE_X_OFFSET + SELECT_BUTTON_WIDTH*MENU_PAGE_CYCLE_SCALE , pageCycleUpY - 0.003 - 0.003*SELECT_BUTTON_SCALE*MENU_PAGE_CYCLE_SCALE + MENU_PAGE_CYCLE_Y_OFFSET )
call BlzFrameSetText( heroSelectionButton[PAGE_UP] , "Next" )
call BlzFrameSetScale( heroSelectionButton[PAGE_UP] , SELECT_BUTTON_SCALE*MENU_PAGE_CYCLE_SCALE )
else
set heroSelectionButton[PAGE_UP] = BlzCreateFrameByType("GLUETEXTBUTTON", "pageUpButton" , heroSelectionMenu, "ScriptDialogButton", 0)
call BlzFrameSetPoint( heroSelectionButton[PAGE_UP] , FRAMEPOINT_TOPLEFT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , pageCycleUpX - MENU_PAGE_CYCLE_X_OFFSET - glueTextOffset + pageCycleScaleOffset , pageCycleUpY + glueTextOffset + MENU_PAGE_CYCLE_Y_OFFSET - pageCycleScaleOffset )
call BlzFrameSetPoint( heroSelectionButton[PAGE_UP] , FRAMEPOINT_BOTTOMRIGHT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , pageCycleUpX - MENU_PAGE_CYCLE_X_OFFSET + pageCycleButtonSize + glueTextOffset + pageCycleScaleOffset , pageCycleUpY - pageCycleButtonSize - glueTextOffset + MENU_PAGE_CYCLE_Y_OFFSET - pageCycleScaleOffset )
call SetButtonFrames(PAGE_UP)
call SetButtonTextures(PAGE_UP, not PRE_SELECT_BEFORE_ENABLED)
call SetButtonTooltip(PAGE_UP, "Page Up", "Go to the next page.")
endif
call BlzTriggerRegisterFrameEvent( pageCycleTrigger, heroSelectionButton[PAGE_UP] , FRAMEEVENT_CONTROL_CLICK )
endif
//Highlight
set heroSelectionButtonHighlight = BlzCreateFrameByType("SPRITE", "SpriteName", heroSelectionMenu, "", 0)
call BlzFrameSetModel(heroSelectionButtonHighlight, "UI\\Feedback\\Autocast\\UI-ModalButtonOn.mdl", 0)
call BlzFrameSetScale(heroSelectionButtonHighlight, MENU_BUTTON_SIZE/0.039)
call BlzFrameSetVisible( heroSelectionButtonHighlight , false )
//Accept
set heroAcceptButton = BlzCreateFrameByType("GLUETEXTBUTTON", "heroAcceptButton" , heroSelectionMenu, "ScriptDialogButton", 0)
call BlzFrameSetPoint( heroAcceptButton , FRAMEPOINT_TOPLEFT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal + menuWidth/2 - SELECT_BUTTON_WIDTH/2 , MENU_Y_TOP - menuHeight + 0.012 + 0.012*SELECT_BUTTON_SCALE )
call BlzFrameSetPoint( heroAcceptButton , FRAMEPOINT_BOTTOMRIGHT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal + menuWidth/2 + SELECT_BUTTON_WIDTH/2 , MENU_Y_TOP - menuHeight - 0.003 - 0.003*SELECT_BUTTON_SCALE )
call BlzFrameSetText( heroAcceptButton , SELECT_BUTTON_TEXT )
call BlzFrameSetScale( heroAcceptButton , SELECT_BUTTON_SCALE )
call BlzFrameSetEnable( heroAcceptButton , false )
set trig = CreateTrigger()
call BlzTriggerRegisterFrameEvent( trig , heroAcceptButton , FRAMEEVENT_CONTROL_CLICK )
call TriggerAddAction( trig, function PickHero )
set trig = null
//Ban
set heroBanButton = BlzCreateFrameByType("GLUETEXTBUTTON", "heroBanButton" , heroSelectionMenu, "ScriptDialogButton", 0)
call BlzFrameSetPoint( heroBanButton , FRAMEPOINT_TOPLEFT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal + menuWidth/2 - SELECT_BUTTON_WIDTH/2 , MENU_Y_TOP - menuHeight + 0.012 + 0.012*SELECT_BUTTON_SCALE )
call BlzFrameSetPoint( heroBanButton , FRAMEPOINT_BOTTOMRIGHT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , menuXLeftLocal + menuWidth/2 + SELECT_BUTTON_WIDTH/2 , MENU_Y_TOP - menuHeight - 0.003 - 0.003*SELECT_BUTTON_SCALE )
call BlzFrameSetText( heroBanButton , SELECT_BUTTON_TEXT )
call BlzFrameSetText( heroBanButton , "Ban" )
call BlzFrameSetEnable( heroBanButton , false )
call BlzFrameSetVisible( heroBanButton , false)
set trig = CreateTrigger()
call BlzTriggerRegisterFrameEvent( trig , heroBanButton , FRAMEEVENT_CONTROL_CLICK )
call TriggerAddAction( trig, function BanHero )
set trig = null
//Ability Buttons
set i = 1
loop
exitwhen i > NUMBER_OF_ABILITY_FRAMES
set heroSelectionAbility[i] = BlzCreateFrameByType("BACKDROP", "heroSelectionAbility_" + I2S(i), heroSelectionMenu, "", 0)
static if ABILITY_BUTTON_HORIZONTAL_LAYOUT then
call BlzFrameSetPoint(heroSelectionAbility[i], FRAMEPOINT_TOPLEFT, heroSelectionMenu , FRAMEPOINT_TOPRIGHT, HERO_ABILITY_PREVIEW_BUTTON_X + HERO_ABILITY_PREVIEW_BUTTON_SIZE*(i-1), HERO_ABILITY_PREVIEW_BUTTON_Y)
else
call BlzFrameSetPoint(heroSelectionAbility[i], FRAMEPOINT_TOPLEFT, heroSelectionMenu , FRAMEPOINT_TOPRIGHT, HERO_ABILITY_PREVIEW_BUTTON_X, HERO_ABILITY_PREVIEW_BUTTON_Y - HERO_ABILITY_PREVIEW_BUTTON_SIZE*(i-1))
endif
call BlzFrameSetSize(heroSelectionAbility[i], HERO_ABILITY_PREVIEW_BUTTON_SIZE, HERO_ABILITY_PREVIEW_BUTTON_SIZE)
call BlzFrameSetVisible( heroSelectionAbility[i] , false )
set heroSelectionAbilityHover[i] = BlzCreateFrameByType("FRAME" , "heroIconFrameHover" , heroSelectionAbility[i] , "" , 0)
call BlzFrameSetAllPoints( heroSelectionAbilityHover[i] , heroSelectionAbility[i] )
set heroSelectionAbilityTooltip[i] = BlzCreateFrame("CustomTooltip", heroSelectionAbilityHover[i], 0, 0)
if TOOLTIP_LOCK_TOP then
call BlzFrameSetPoint( heroSelectionAbilityTooltip[i] , FRAMEPOINT_TOPLEFT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , tooltipLeftXLocal , TOOLTIP_Y )
else
call BlzFrameSetPoint( heroSelectionAbilityTooltip[i] , FRAMEPOINT_BOTTOMLEFT , fullScreenFrame , FRAMEPOINT_BOTTOMLEFT , tooltipLeftXLocal , TOOLTIP_Y )
endif
call BlzFrameSetTooltip( heroSelectionAbilityHover[i] , heroSelectionAbilityTooltip[i] )
call BlzFrameSetSize( heroSelectionAbilityTooltip[i] , TOOLTIP_WIDTH , 0.0 )
set heroSelectionAbilityTooltipTitle[i] = BlzFrameGetChild( heroSelectionAbilityTooltip[i],0)
set heroSelectionAbilityTooltipText[i] = BlzFrameGetChild( heroSelectionAbilityTooltip[i],1)
set i = i + 1
endloop
call BlzFrameSetVisible( heroSelectionMenu, false)
call NoCrash("InitMenu")
endfunction
//==========================================================================================================================================================
//API
//==========================================================================================================================================================
function EveryoneHasHero takes nothing returns boolean
return numPlayersWithHero == numSelectingPlayers
endfunction
function NoOneInHeroSelection takes nothing returns boolean
return numPlayersInSelection == 0
endfunction
function EnablePlayerHeroSelection takes player whichPlayer, boolean enable returns nothing
local integer i
local integer P = GetPlayerId(whichPlayer)
if not isInHeroSelection[P] then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to enable hero selection for player who is not in hero selection...")
endif
return
endif
call InitCrashCheck("EnablePlayerHeroSelection")
if enable then
if localPlayerId == P then
call BlzFrameSetVisible(heroSelectionMenu, true)
set i = 1
loop
exitwhen i > PAGE_UP
call SetButtonTextures(i, false)
set i = i + 1
endloop
endif
static if PHANTOM_HERO_WHEN_CANNOT_BE_PICKED then
call BlzSetSpecialEffectColor( selectedHero[localPlayerId] , 255 , 255 , 255 )
call BlzSetSpecialEffectAlpha( selectedHero[localPlayerId] , 255 )
endif
else
if localPlayerId == P then
set i = 1
loop
exitwhen i > NUMBER_OF_ABILITY_FRAMES
call BlzFrameSetVisible( heroSelectionAbility[i] , false )
set i = i + 1
endloop
set i = 1
loop
exitwhen i > PAGE_UP
call SetButtonTextures(i, true)
set i = i + 1
endloop
call BlzFrameSetEnable(heroAcceptButton, false)
endif
static if PHANTOM_HERO_WHEN_CANNOT_BE_PICKED then
call BlzSetSpecialEffectColor( selectedHero[localPlayerId] , 0 , 0 , 0 )
call BlzSetSpecialEffectAlpha( selectedHero[localPlayerId] , 128 )
endif
set preselectedHeroIndex[P] = 0
endif
set heroSelectionDisabledForPlayer[P] = not enable
call NoCrash("EnablePlayerHeroSelection")
endfunction
function EnablePlayerHeroPreselection takes player whichPlayer, boolean enable returns nothing
local integer i
local integer P = GetPlayerId(whichPlayer)
if not isInHeroSelection[P] then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to enable hero preselection for player who is not in hero selection...")
endif
return
endif
call InitCrashCheck("EnablePlayerHeroPreselection")
if enable then
if GetLocalPlayer() == whichPlayer then
set i = 1
loop
exitwhen i > PAGE_UP
call SetButtonTextures(i, false)
set i = i + 1
endloop
endif
else
if GetLocalPlayer() == whichPlayer then
set i = 1
loop
exitwhen i > NUMBER_OF_ABILITY_FRAMES
call BlzFrameSetVisible( heroSelectionAbility[i] , false )
set i = i + 1
endloop
set i = 1
loop
exitwhen i > PAGE_UP
call SetButtonTextures(i, true)
set i = i + 1
endloop
call BlzFrameSetVisible( heroSelectionButtonHighlight, false)
call BlzFrameSetEnable(heroAcceptButton, false)
endif
call BlzSetSpecialEffectPosition(selectedHero[P], GARBAGE_DUMP_X, GARBAGE_DUMP_Y, 0)
call DestroyEffect(selectedHero[P])
call DestroyEffect(selectedHeroGlow[P])
set preselectedHeroIndex[P] = 0
endif
set heroPreselectionDisabledForPlayer[P] = not enable
call NoCrash("EnablePlayerHeroPreselection")
endfunction
function HeroSelectionAllRandom takes nothing returns nothing
local integer i = 1
local integer P
local noArgCallback onAllRandom = HeroSelectionAllRandom
if numPlayersInSelection == 0 then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to start hero ban phase, but no one is in hero selection right now...")
endif
return
endif
set storeHeroIndex = RANDOM_HERO
set isForcedSelect = true
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
if not playerHasHero[P] then
set storePlayerIndex = P
call PickHero()
endif
set i = i + 1
endloop
call onAllRandom.evaluate()
set isForcedSelect = false
endfunction
function HeroSelectionPlayerForceSelect takes player whichPlayer, Hero whichHero returns nothing
if not isInHeroSelection[GetPlayerId(whichPlayer)] then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to force select on a player who is not in hero selection...")
endif
return
endif
if playerHasHero[GetPlayerId(whichPlayer)] then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to force select on a player who already selected a hero...")
endif
return
endif
set isForcedSelect = true
set storePlayerIndex = GetPlayerId(whichPlayer)
set storeHeroIndex = whichHero.index
call PickHero()
set isForcedSelect = false
endfunction
function HeroSelectionPlayerForceRandom takes player whichPlayer returns nothing
if not isInHeroSelection[GetPlayerId(whichPlayer)] then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to force random on a player who is not in hero selection...")
endif
return
endif
if playerHasHero[GetPlayerId(whichPlayer)] then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to force random on a player who already selected a hero...")
endif
return
endif
set isForcedSelect = true
set storePlayerIndex = GetPlayerId(whichPlayer)
set storeHeroIndex = RANDOM_HERO
call PickHero()
set isForcedSelect = false
endfunction
function HeroSelectionForceEnd takes nothing returns nothing
local integer i = 1
local integer P
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
if not playerHasHero[P] then
call HeroSelectionPlayerForceRandom(Player(P))
endif
set i = i + 1
endloop
endfunction
function PlayerEscapeHeroSelection takes player whichPlayer returns nothing
if not isInHeroSelection[GetPlayerId(whichPlayer)] then
return
endif
call EscapePlayer(whichPlayer)
endfunction
function BanHeroFromSelectionForPlayer takes Hero whichHero, boolean disable, player whichPlayer returns nothing
call ExecuteBan(whichHero, disable, GetPlayerId(whichPlayer))
endfunction
function BanHeroFromSelection takes Hero whichHero, boolean disable returns nothing
call ExecuteBan(whichHero, disable, -1)
endfunction
function HeroSelectionSetTimeRemaining takes real time returns nothing
call TimerStart(countdownTimer, time, false, function TimeExpires)
call BlzFrameSetText(timerFrame, TIMER_TEXT + GetClockString(TimerGetRemaining(countdownTimer)))
call BlzFrameSetVisible(timerFrame, isInHeroSelection[localPlayerId])
endfunction
function HeroSelectionAddTimeRemaining takes real time returns nothing
call TimerStart(countdownTimer, TimerGetRemaining(countdownTimer) + time, false, function TimeExpires)
call BlzFrameSetText(timerFrame, TIMER_TEXT + GetClockString(TimerGetRemaining(countdownTimer)))
call BlzFrameSetVisible(timerFrame, isInHeroSelection[localPlayerId])
endfunction
function EnableHeroSelection takes nothing returns nothing
local integer i
local integer P
local noArgCallback onEnable = HeroSelectionOnEnable
if numPlayersInSelection == 0 then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to enable hero selection, but no one is in hero selection right now...")
endif
return
endif
call InitCrashCheck("EnableHeroSelection")
if TIME_LIMIT != 0 then
call BlzFrameSetVisible(timerFrame, isInHeroSelection[localPlayerId])
call TimerStart(countdownTimer, TIME_LIMIT, false, function TimeExpires)
call TimerStart(countdownUpdateTimer, 1.0, true, function UpdateTimerFrame)
call BlzFrameSetText(timerFrame, TIMER_TEXT + GetClockString(TimerGetRemaining(countdownTimer)))
endif
set i = 1
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
if isInHeroSelection[P] then
set heroSelectionDisabledForPlayer[P] = false
set heroPreselectionDisabledForPlayer[P] = false
set playerHasBan[P] = false
if localPlayerId == P then
call BlzFrameSetVisible(heroSelectionMenu, true)
endif
endif
static if PLAYER_TEXT_TAGS then
set backgroundHeroTextTag[P] = CreateTextTag()
call SetTextTagText(backgroundHeroTextTag[P], coloredPlayerName[P] , 0.023)
call SetTextTagPos(backgroundHeroTextTag[P], BACKGROUND_HERO_X[P], BACKGROUND_HERO_Y[P], 25)
call SetTextTagVisibility(backgroundHeroTextTag[P], true)
endif
set i = i + 1
endloop
call BlzFrameSetVisible(heroAcceptButton, true )
call BlzFrameSetVisible(heroBanButton, false )
call BlzFrameSetEnable( heroBanButton, false )
set captionAlphaMultiplier = 1
if HERO_SELECTION_CAPTION != null then
call BlzFrameSetVisible(captionFrame, isInHeroSelection[localPlayerId])
if CAPTION_COLOR_1 != CAPTION_COLOR_2 or CAPTION_ALPHA_1 != CAPTION_ALPHA_2 then
call AnimateCaption()
endif
endif
if HeroCanBePicked(preselectedHeroIndex[localPlayerId], localPlayerId) then
call BlzFrameSetEnable( heroAcceptButton, true )
static if PHANTOM_HERO_WHEN_CANNOT_BE_PICKED then
call BlzSetSpecialEffectColor( selectedHero[localPlayerId] , 255 , 255 , 255 )
call BlzSetSpecialEffectAlpha( selectedHero[localPlayerId] , 255 )
endif
endif
set inBanPhase = false
set i = 1
loop
exitwhen i > NUMBER_OF_HEROES
call SetButtonTextures(i, not HeroCanBePicked(i, localPlayerId))
set i = i + 1
endloop
call SetButtonTextures(RANDOM_HERO, false)
call SetButtonTextures(SUGGEST_RANDOM, false)
call SetButtonTextures(PAGE_DOWN, false)
call SetButtonTextures(PAGE_UP, false)
call onEnable.evaluate()
call NoCrash("EnableHeroSelection")
endfunction
function StartHeroSelectionTimer takes real timeout, code callback returns nothing
call TimerStart( countdownTimer, timeout, false, callback)
call BlzFrameSetVisible(timerFrame, isInHeroSelection[localPlayerId])
call TimerStart(countdownUpdateTimer, 1.0, true, function UpdateTimerFrame)
call BlzFrameSetText(timerFrame, TIMER_TEXT + GetClockString(TimerGetRemaining(countdownTimer)))
endfunction
function StartHeroBanPhase takes real timeLimit returns nothing
local integer i = 1
local integer P
if numPlayersInSelection == 0 then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to start hero ban phase, but no one is in hero selection right now...")
endif
return
endif
call InitCrashCheck("StartHeroBanPhase")
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
set playerHasBan[P] = true
set heroPreselectionDisabledForPlayer[P] = false
if localPlayerId == P then
call BlzFrameSetVisible(heroSelectionMenu, true)
endif
set i = i + 1
endloop
set inBanPhase = true
set i = 1
loop
exitwhen i > NUMBER_OF_HEROES
call SetButtonTextures(i, not HeroCanBePicked(i, localPlayerId))
set i = i + 1
endloop
call SetButtonTextures(SUGGEST_RANDOM, false)
call SetButtonTextures(PAGE_DOWN, false)
call SetButtonTextures(PAGE_UP, false)
call BlzFrameSetVisible(heroAcceptButton, false)
call BlzFrameSetVisible(heroBanButton, true)
call BlzFrameSetEnable( heroBanButton , HeroCanBePicked(preselectedHeroIndex[localPlayerId], localPlayerId) and preselectedHeroIndex[P] != RANDOM_HERO )
call StartHeroSelectionTimer(timeLimit, function EnableHeroSelection)
call NoCrash("StartHeroBanPhase")
endfunction
function PlayerReturnToHeroSelection takes player whichPlayer returns nothing
local integer i
local integer P = GetPlayerId(whichPlayer)
local integer id
local playerCallback onReturn = HeroSelectionOnReturn
local framehandle consoleUIBackdrop = BlzGetFrameByName("ConsoleUIBackdrop",0)
local framehandle bottomUI = BlzGetFrameByName("ConsoleBottomBar", 0)
local framehandle topUI = BlzGetFrameByName("ConsoleTopBar", 0)
if heroSelectionMenu == null then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to return player to hero selection, but hero selection was not initialized...")
endif
return
endif
call InitCrashCheck("PlayerReturnToHeroSelection")
set playerHasHero[P] = false
set isInHeroSelection[P] = true
set isRepicking[P] = true
set numPlayersWithHero = numPlayersWithHero - 1
set numPlayersInSelection = numPlayersInSelection + 1
set heroIndexWasPicked[pickedHeroIndex[P]] = false
set pickedHeroIndex[P] = 0
static if not DELETE_BACKGROUND_HEROES_AFTER_END then
call BlzSetSpecialEffectPosition(backgroundHero[P], GARBAGE_DUMP_X, GARBAGE_DUMP_Y, 0)
call DeleteBackgroundHero(P)
endif
set i = 1
loop
exitwhen i > NUMBER_OF_HEROES
call SetButtonTextures(i, not HeroCanBePicked(Hero.list[i], localPlayerId))
set i = i + 1
endloop
call SetButtonTextures(RANDOM_HERO, false)
call SetButtonTextures(SUGGEST_RANDOM, false)
call SetButtonTextures(PAGE_DOWN, false)
call SetButtonTextures(PAGE_UP, false)
if localPlayerId == P then
call ClearSelection()
call BlzFrameSetVisible(heroSelectionMenu, true)
call BlzFrameSetVisible( heroSelectionButtonHighlight , false )
static if HIDE_GAME_UI then
call BlzHideOriginFrames(true)
call BlzFrameSetAllPoints(BlzGetOriginFrame(ORIGIN_FRAME_WORLD_FRAME, 0), BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0))
call BlzFrameSetVisible(consoleUIBackdrop, false)
call BlzFrameSetVisible(bottomUI, false)
call BlzFrameSetVisible(topUI, false)
endif
endif
static if CREATE_SHADOWS then
call RemoveDestructable(foregroundHeroShadow)
if preselectedHeroIndex[localPlayerId] == 0 or playerHasHero[localPlayerId] then
set id = NO_SHADOW_DESTRUCTABLE_ID
else
set id = SHADOW_DESTRUCTABLE_ID
endif
set foregroundHeroShadow = CreateDestructable(id, localForegroundHeroX, localForegroundHeroY, 0, 1, 0)
endif
static if ENFORCE_CAMERA then
call TimerStart(lockCameraTimer, 0.01, true, function LockSelecterCamera)
endif
call onReturn.evaluate(whichPlayer)
call NoCrash("PlayerReturnToHeroSelection")
endfunction
function BeginHeroSelection takes nothing returns nothing
local integer i = 1
local integer P
local noArgCallback onBegin = HeroSelectionOnBegin
if heroSelectionMenu == null then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to begin hero selection, but hero selection was not initialized...")
endif
return
endif
call InitCrashCheck("BeginHeroSelection")
static if HIDE_GAME_UI then
call BlzHideOriginFrames(true)
call BlzFrameSetAllPoints(BlzGetOriginFrame(ORIGIN_FRAME_WORLD_FRAME, 0), BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0))
call BlzFrameSetVisible(BlzGetFrameByName("ConsoleUIBackdrop",0), false)
call BlzFrameSetVisible(BlzGetFrameByName("ConsoleBottomBar",0), false)
call BlzFrameSetVisible(BlzGetFrameByName("ConsoleTopBar",0), false)
endif
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
set isInHeroSelection[P] = true
set numPlayersInSelection = numPlayersInSelection + 1
if localPlayerId == P then
call ClearSelection()
endif
set i = i + 1
endloop
static if CREATE_SHADOWS then
set foregroundHeroShadow = CreateDestructable(NO_SHADOW_DESTRUCTABLE_ID, localForegroundHeroX, localForegroundHeroY, 0, 1, 0)
endif
static if SHOW_MENU_BEFORE_ENABLED then
call BlzFrameSetVisible(heroSelectionMenu, true)
endif
call BlzFrameSetVisible( heroSelectionButtonHighlight , false )
static if ENFORCE_CAMERA then
call TimerStart(lockCameraTimer, 0.01, true, function LockSelecterCamera)
endif
call onBegin.evaluate()
call NoCrash("BeginHeroSelection")
endfunction
function RestartHeroSelection takes nothing returns nothing
local integer i = 1
local integer j
local integer P
local noArgCallback onRestart = HeroSelectionOnRestart
if heroSelectionMenu == null then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to restart hero selection, but hero selection was not initialized...")
endif
return
endif
if numPlayersInSelection > 0 then
static if HERO_SELECTION_ENABLE_DEBUG_MODE then
call BJDebugMsg("|cffff0000Warning:|r Attempted to restart hero selection while there are players in hero selection...")
endif
return
endif
call InitCrashCheck("RestartHeroSelection")
call onRestart.evaluate()
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
set playerHasHero[P] = false
set playerHasBan[P] = false
set isInHeroSelection[P] = false
set heroSelectionDisabledForPlayer[P] = true
set pickedHeroIndex[P] = 0
static if CREATE_BACKGROUND_HEROES and not DELETE_BACKGROUND_HEROES_AFTER_END then
call BlzSetSpecialEffectPosition(backgroundHero[P], GARBAGE_DUMP_X, GARBAGE_DUMP_Y, 0)
call DeleteBackgroundHero(P)
endif
set i = i + 1
endloop
set i = 1
loop
exitwhen i > NUMBER_OF_HEROES
set heroIndexWasPicked[i] = false
set j = 1
loop
exitwhen j > numSelectingPlayers
set P = playerNumberOfSlot[i]
set heroIndexWasBanned[i][j] = false
set j = j + 1
endloop
set i = i + 1
endloop
set numPlayersWithHero = 0
set numPlayersInSelection = 0
call NoCrash("RestartHeroSelection")
call BeginHeroSelection()
endfunction
function InitHeroSelection takes nothing returns nothing
local integer i = 0
local integer j
local integer P
local trigger leaveTrigger = CreateTrigger()
local integer array teamSize
local integer array slotInTeam
local location tempLoc
local real array cameraTargetPositionX
local real array cameraTargetPositionY
local real cameraEyePositionX
local real cameraEyePositionY
call InitCrashCheck("InitHeroSelection")
call InitFullScreenParents()
set GARBAGE_DUMP_X = GetRectMaxX(GetPlayableMapRect())
set GARBAGE_DUMP_Y = GetRectMaxY(GetPlayableMapRect())
set localPlayerId = GetPlayerId(GetLocalPlayer())
call HeroSelectionInitPlayers()
call HeroSelectionSetPlayerColors()
//Player List
set i = 0
loop
exitwhen i > 23
static if not AUTO_SET_SELECTING_PLAYERS then
if PLAYER_SELECTS_HERO[i] and GetPlayerSlotState(Player(i)) == PLAYER_SLOT_STATE_PLAYING then
set numSelectingPlayers = numSelectingPlayers + 1
set playerNumberOfSlot[numSelectingPlayers] = i
set coloredPlayerName[i] = PLAYER_COLOR[i] + GetPlayerName(Player(i)) + "|r"
set heroPreselectionDisabledForPlayer[i] = not PRE_SELECT_BEFORE_ENABLED
set heroSelectionDisabledForPlayer[i] = true
if GetPlayerController(Player(i)) == MAP_CONTROL_USER then
set playerIsHuman[i] = true
set numSelectingHumans = numSelectingHumans + 1
call TriggerRegisterPlayerEvent(leaveTrigger, Player(i), EVENT_PLAYER_LEAVE)
endif
set j = 1
loop
exitwhen j > NUMBER_OF_HEROES
set heroIndexWasBanned[j][i] = false
set j = j + 1
endloop
endif
else
if GetPlayerSlotState(Player(i)) == PLAYER_SLOT_STATE_PLAYING and GetPlayerController(Player(i)) == MAP_CONTROL_USER then
set numSelectingPlayers = numSelectingPlayers + 1
set playerNumberOfSlot[numSelectingPlayers] = i
set coloredPlayerName[i] = PLAYER_COLOR[i] + GetPlayerName(Player(i)) + "|r"
set heroPreselectionDisabledForPlayer[i] = not PRE_SELECT_BEFORE_ENABLED
set heroSelectionDisabledForPlayer[i] = true
set playerIsHuman[i] = true
set numSelectingHumans = numSelectingHumans + 1
call TriggerRegisterPlayerEvent(leaveTrigger, Player(i), EVENT_PLAYER_LEAVE)
set j = 1
loop
exitwhen j > NUMBER_OF_HEROES
set heroIndexWasBanned[j][i] = false
set j = j + 1
endloop
endif
endif
set i = i + 1
endloop
set NUMBER_OF_TEAMS = 0
set i = 1
loop
exitwhen i > numSelectingPlayers
set NUMBER_OF_TEAMS = IMaxBJ(NUMBER_OF_TEAMS, TEAM_OF_PLAYER[playerNumberOfSlot[i]])
set i = i + 1
endloop
//Foreground hero locations are asynchronous. Background hero locations are not.
static if SEPARATE_LOCATIONS_FOR_EACH_TEAM then
call HeroSelectionInitArrays()
set localForegroundHeroX = TEAM_FOREGROUND_HERO_X[TEAM_OF_PLAYER[localPlayerId]]
set localForegroundHeroY = TEAM_FOREGROUND_HERO_Y[TEAM_OF_PLAYER[localPlayerId]]
set localHeroSelectionAngle = TEAM_HERO_SELECTION_ANGLE[TEAM_OF_PLAYER[localPlayerId]]
set i = 1
loop
exitwhen i > NUMBER_OF_TEAMS
set cameraTargetPositionX[i] = TEAM_FOREGROUND_HERO_X[i] + Cos(Deg2Rad(TEAM_FOREGROUND_HERO_X[i]))*CAMERA_TARGET_OFFSET + Sin(Deg2Rad(TEAM_HERO_SELECTION_ANGLE[i]))*CAMERA_PERPENDICULAR_OFFSET
set cameraTargetPositionY[i] = TEAM_FOREGROUND_HERO_Y[i] + Sin(Deg2Rad(TEAM_FOREGROUND_HERO_Y[i]))*CAMERA_TARGET_OFFSET - Cos(Deg2Rad(TEAM_HERO_SELECTION_ANGLE[i]))*CAMERA_PERPENDICULAR_OFFSET
set i = i + 1
endloop
else
set localForegroundHeroX = FOREGROUND_HERO_X
set localForegroundHeroY = FOREGROUND_HERO_Y
set localHeroSelectionAngle = HERO_SELECTION_ANGLE
set i = 1
loop
exitwhen i > NUMBER_OF_TEAMS
set cameraTargetPositionX[i] = FOREGROUND_HERO_X + Cos(Deg2Rad(localHeroSelectionAngle))*CAMERA_TARGET_OFFSET + Sin(Deg2Rad(HERO_SELECTION_ANGLE))*CAMERA_PERPENDICULAR_OFFSET
set cameraTargetPositionY[i] = FOREGROUND_HERO_Y + Sin(Deg2Rad(localHeroSelectionAngle))*CAMERA_TARGET_OFFSET - Cos(Deg2Rad(HERO_SELECTION_ANGLE))*CAMERA_PERPENDICULAR_OFFSET
set i = i + 1
endloop
endif
call TriggerAddAction(leaveTrigger, function OnPlayerLeave)
set leaveTrigger = null
call SetCategories()
call HeroDeclaration()
set NUMBER_OF_HEROES = Hero.numHeroes
set RANDOM_HERO = NUMBER_OF_HEROES + 1
set SUGGEST_RANDOM = NUMBER_OF_HEROES + 2
set PAGE_DOWN = NUMBER_OF_HEROES + 3
set PAGE_UP = NUMBER_OF_HEROES + 4
set i = 1
loop
exitwhen i > NUMBER_OF_HEROES
set j = 0
loop
set j = j + 1
exitwhen Hero.list[i].abilities[j] == 0
endloop
if j - 1 > NUMBER_OF_ABILITY_FRAMES then
set NUMBER_OF_ABILITY_FRAMES = j - 1
endif
if Hero.list[i].category > NUMBER_OF_CATEGORIES then
set NUMBER_OF_CATEGORIES = Hero.list[i].category
endif
call Hero.list[i].GetValues()
set i = i + 1
endloop
set i = 1
loop
exitwhen i > NUMBER_OF_CATEGORIES
set NUMBER_OF_PAGES = IMaxBJ(NUMBER_OF_PAGES, PAGE_OF_CATEGORY[i])
set i = i + 1
endloop
//Camera
call CameraSetupSetField(heroSelectionCamera , CAMERA_FIELD_ZOFFSET , CAMERA_Z_OFFSET , 0.0)
call CameraSetupSetField(heroSelectionCamera , CAMERA_FIELD_ANGLE_OF_ATTACK , 360 - CAMERA_PITCH , 0.0)
call CameraSetupSetField(heroSelectionCamera , CAMERA_FIELD_TARGET_DISTANCE , CAMERA_DISTANCE , 0.0)
call CameraSetupSetField(heroSelectionCamera , CAMERA_FIELD_ROLL , 0.0 , 0.0)
call CameraSetupSetField(heroSelectionCamera , CAMERA_FIELD_FIELD_OF_VIEW , CAMERA_FIELD_OF_VIEW , 0.0)
call CameraSetupSetField(heroSelectionCamera , CAMERA_FIELD_FARZ , 7500.0 , 0.0)
call CameraSetupSetField(heroSelectionCamera , CAMERA_FIELD_ROTATION , localHeroSelectionAngle , 0.0)
call CameraSetupSetDestPosition(heroSelectionCamera , cameraTargetPositionX[TEAM_OF_PLAYER[localPlayerId]] , cameraTargetPositionY[TEAM_OF_PLAYER[localPlayerId]] , 0.0)
//Background Heroes
set i = 1
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
set teamSize[TEAM_OF_PLAYER[P]] = teamSize[TEAM_OF_PLAYER[P]] + 1
set slotInTeam[P] = teamSize[TEAM_OF_PLAYER[P]]
set i = i + 1
endloop
set i = 1
loop
exitwhen i > numSelectingPlayers
set P = playerNumberOfSlot[i]
set tempLoc = GetPlayerBackgroundHeroLocation(P, i, numSelectingPlayers, TEAM_OF_PLAYER[P], slotInTeam[P], teamSize[TEAM_OF_PLAYER[P]])
static if SEPARATE_LOCATIONS_FOR_EACH_TEAM then
set cameraEyePositionX = cameraTargetPositionX[TEAM_OF_PLAYER[P]] - Cos(Deg2Rad(CAMERA_PITCH))*Cos(Deg2Rad(TEAM_HERO_SELECTION_ANGLE[TEAM_OF_PLAYER[P]]))*CAMERA_DISTANCE
set cameraEyePositionY = cameraTargetPositionY[TEAM_OF_PLAYER[P]] - Cos(Deg2Rad(CAMERA_PITCH))*Sin(Deg2Rad(TEAM_HERO_SELECTION_ANGLE[TEAM_OF_PLAYER[P]]))*CAMERA_DISTANCE
set BACKGROUND_HERO_X[P] = cameraEyePositionX - Sin(Deg2Rad(TEAM_HERO_SELECTION_ANGLE[TEAM_OF_PLAYER[P]]))*GetLocationX(tempLoc) + Cos(Deg2Rad(TEAM_HERO_SELECTION_ANGLE[TEAM_OF_PLAYER[P]]))*GetLocationY(tempLoc)
set BACKGROUND_HERO_Y[P] = cameraEyePositionY + Cos(Deg2Rad(TEAM_HERO_SELECTION_ANGLE[TEAM_OF_PLAYER[P]]))*GetLocationX(tempLoc) + Sin(Deg2Rad(TEAM_HERO_SELECTION_ANGLE[TEAM_OF_PLAYER[P]]))*GetLocationY(tempLoc)
else
set cameraEyePositionX = cameraTargetPositionX[TEAM_OF_PLAYER[P]] - Cos(Deg2Rad(CAMERA_PITCH))*Cos(Deg2Rad(HERO_SELECTION_ANGLE))*CAMERA_DISTANCE
set cameraEyePositionY = cameraTargetPositionY[TEAM_OF_PLAYER[P]] - Cos(Deg2Rad(CAMERA_PITCH))*Sin(Deg2Rad(HERO_SELECTION_ANGLE))*CAMERA_DISTANCE
set BACKGROUND_HERO_X[P] = cameraEyePositionX - Sin(Deg2Rad(HERO_SELECTION_ANGLE))*GetLocationX(tempLoc) + Cos(Deg2Rad(HERO_SELECTION_ANGLE))*GetLocationY(tempLoc)
set BACKGROUND_HERO_Y[P] = cameraEyePositionY + Cos(Deg2Rad(HERO_SELECTION_ANGLE))*GetLocationX(tempLoc) + Sin(Deg2Rad(HERO_SELECTION_ANGLE))*GetLocationY(tempLoc)
endif
call RemoveLocation(tempLoc)
set i = i + 1
endloop
set tempLoc = null
//Caption
set captionFrame = BlzCreateFrameByType("TEXT", "captionFrame", BlzGetOriginFrame(ORIGIN_FRAME_WORLD_FRAME, 0), "", 0)
call BlzFrameSetVisible(captionFrame , false)
call BlzFrameSetEnable(captionFrame , false)
call BlzFrameSetSize(captionFrame, 0.4, 0.05)
call BlzFrameSetAbsPoint(captionFrame , FRAMEPOINT_CENTER , CAPTION_X , CAPTION_Y)
call BlzFrameSetText(captionFrame, CAPTION_COLOR_1 + HERO_SELECTION_CAPTION + "|r")
call BlzFrameSetTextAlignment(captionFrame , TEXT_JUSTIFY_MIDDLE , TEXT_JUSTIFY_CENTER)
call BlzFrameSetScale(captionFrame, CAPTION_FONT_SIZE/10.)
call BlzFrameSetAlpha(captionFrame , CAPTION_ALPHA_1)
//Timer
set timerFrame = BlzCreateFrameByType("TEXT", "timerFrame", BlzGetOriginFrame(ORIGIN_FRAME_WORLD_FRAME, 0), "", 0)
call BlzFrameSetVisible(timerFrame , false)
call BlzFrameSetEnable(timerFrame , false)
call BlzFrameSetSize(timerFrame, 0.15, 0.05)
call BlzFrameSetAbsPoint(timerFrame , FRAMEPOINT_CENTER , TIMER_X , TIMER_Y)
call BlzFrameSetTextAlignment(timerFrame , TEXT_JUSTIFY_MIDDLE , TEXT_JUSTIFY_CENTER)
call BlzFrameSetScale(timerFrame, TIMER_FONT_SIZE/10.)
call NoCrash("InitHeroSelection")
call InitMenu()
endfunction
endlibrary
library TestHeroSelection initializer Init requires HeroSelection
globals
private integer loopInt = 0
constant real HERO_SPAWN_X = -3000
constant real HERO_SPAWN_Y = 1300
NeatWindow centerWindow
unit array playerHero
effect array circles
endglobals
private function MusicFadeOutLoop takes nothing returns nothing
set loopInt = loopInt + 1
if loopInt < 100 then
call SetMusicVolumeBJ(100 - loopInt)
else
call DestroyTimer(GetExpiredTimer())
call ClearMapMusic()
call StopMusic(false)
call SetMusicVolumeBJ(100)
endif
endfunction
function MusicSlowFadeOut takes nothing returns nothing
call TimerStart(CreateTimer(), 0.05, true , function MusicFadeOutLoop)
set loopInt = 0
endfunction
function BeginGame takes nothing returns nothing
call PlayMusic("Sound\\Music\\mp3Music\\NightElf3.mp3")
call DisplayTextToForce(GetPlayersAll(), "Let the battle begin!")
endfunction
private function PickBots takes nothing returns nothing
set loopInt = loopInt + 1
if loopInt < 6 then
call HeroSelectionPlayerForceRandom(Player(loopInt))
else
call DestroyTimer(GetExpiredTimer())
endif
endfunction
private function Enable takes nothing returns nothing
call EnableHeroSelection()
call TimerStart(GetExpiredTimer(), 2.0, true , function PickBots)
endfunction
private function Start takes nothing returns nothing
set centerWindow = NeatWindow.create(TEXT_MESSAGE_X_POSITION, 0.22, TEXT_MESSAGE_BLOCK_WIDTH, 0.2, 1, false)
call InitHeroSelection()
call BeginHeroSelection()
call TimerStart(GetExpiredTimer(), 7.0, false , function Enable)
endfunction
private function Vendor takes nothing returns nothing
local integer i = 0
if GetItemTypeId(GetManipulatedItem()) == 'stwp' then
call PlayerReturnToHeroSelection(GetOwningPlayer(GetTriggerUnit()))
call RemoveUnit(GetTriggerUnit())
else
if NoOneInHeroSelection() then
loop
exitwhen i > 5
call RemoveUnit(playerHero[i])
set i = i + 1
endloop
call RestartHeroSelection()
call TimerStart(CreateTimer(), 7.0, false , function Enable)
else
call BJDebugMsg("Someone is in hero selection right now...")
endif
endif
endfunction
private function Init takes nothing returns nothing
local trigger trig = CreateTrigger()
call TimerStart( CreateTimer(), 0.01, false , function Start )
call FogEnableOff()
call FogMaskEnableOff()
call SetTimeOfDay(0)
call SetTimeOfDayScale(0)
call SetSkyModel("Starsphere.mdx")
call SetDoodadAnimationRect(GetPlayableMapRect(), 'D00O', "stand lumber", true)
call TriggerAddAction(trig, function Vendor)
call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_PICKUP_ITEM)
endfunction
endlibrary