------------------------------------------------------------------------------- -- Title: Mik's Scrolling Battle Text Default Animation Styles -- Author: Mikord ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- -- Imports. ------------------------------------------------------------------------------- -- Local references to various functions for faster access. local math_floor = math.floor local math_ceil = math.ceil local math_random = math.random local math_max = math.max local math_abs = math.abs local math_sqrt = math.sqrt ------------------------------------------------------------------------------- -- Private constants. ------------------------------------------------------------------------------- -- Sticky animation style constants. local POW_FADE_IN_TIME = 0.17 local POW_DISPLAY_TIME = 1.5 local POW_FADE_OUT_TIME = 0.5 local POW_TEXT_DELTA = 0.7 local JIGGLE_DELAY_TIME = 0.05 -- Static animation style constants. local STATIC_DISPLAY_TIME = 3.15 -- Angled animation style constants. local ANGLED_HORIZONTAL_PHASE_TIME = 1 local ANGLED_FADE_OUT_TIME = 0.5 local ANGLED_WIDTH_PERCENT = 0.85 -- Default movement speed. (260 pixels every 3 seconds) local MOVEMENT_SPEED = (3 / 260) -- Minimum amount of space allowed between two strings. local MIN_VERTICAL_SPACING = 8 local MIN_HORIZONTAL_SPACING = 10 ------------------------------------------------------------------------------- -- Private variables. ------------------------------------------------------------------------------- -- Values used for previous events. local lastAngledFinishPositionY = {} local lastAngledDirection = {} local lastHorizontalPositionY = {} local lastHorizontalDirection = {} ------------------------------------------------------------------------------- -- Pow Sticky functions. ------------------------------------------------------------------------------- -- **************************************************************************** -- Animates the passed display event using using the normal pow style. -- **************************************************************************** local function AnimatePowNormal(displayEvent, animationProgress) local fadeInPercent = POW_FADE_IN_TIME / displayEvent.scrollTime -- Scale the text height. if (animationProgress <= fadeInPercent) then displayEvent.fontString:SetTextHeight(displayEvent.fontSize * (1 + ((1 - animationProgress / fadeInPercent) * POW_TEXT_DELTA))) -- Reset the font properties to normal. else local fontPath, _, fontOutline = displayEvent.fontString:GetFont() displayEvent.fontString:SetFont(fontPath, displayEvent.fontSize, fontOutline) end end -- **************************************************************************** -- Animates the passed display event using using the jiggle pow style. -- **************************************************************************** local function AnimatePowJiggle(displayEvent, animationProgress) local fadeInPercent = POW_FADE_IN_TIME / displayEvent.scrollTime -- Scale the text height. if (animationProgress <= fadeInPercent) then displayEvent.fontString:SetTextHeight(displayEvent.fontSize * (1 + ((1 - animationProgress / fadeInPercent) * POW_TEXT_DELTA))) return -- Jiggle the text around and reset the font properties to normal. elseif (animationProgress <= displayEvent.fadePercent) then local elapsedTime = displayEvent.elapsedTime if (elapsedTime - displayEvent.timeLastJiggled > JIGGLE_DELAY_TIME) then displayEvent.positionX = displayEvent.originalPositionX + math_random(-1, 1) displayEvent.positionY = displayEvent.originalPositionY + math_random(-1, 1) displayEvent.timeLastJiggled = elapsedTime end local fontPath, _, fontOutline = displayEvent.fontString:GetFont() displayEvent.fontString:SetFont(fontPath, displayEvent.fontSize, fontOutline) end end -- **************************************************************************** -- Initialize the passed display event and reposition the ones that are -- currently animating in the scroll area to prevent overlaps. -- **************************************************************************** local function InitPow(newDisplayEvent, activeDisplayEvents, direction, behavior) -- Calculate how long the animation should take by only scaling the display period and -- set the percent to start the fade out. local animationSpeed = newDisplayEvent.animationSpeed local scrollTime = POW_FADE_IN_TIME + (POW_DISPLAY_TIME / animationSpeed) + POW_FADE_OUT_TIME newDisplayEvent.scrollTime = scrollTime * animationSpeed newDisplayEvent.fadePercent = (POW_FADE_IN_TIME + (POW_DISPLAY_TIME / animationSpeed)) / scrollTime -- Choose the correct animation function. newDisplayEvent.animationHandler = (behavior == "Jiggle") and AnimatePowJiggle or AnimatePowNormal -- Set the new event's starting position. local anchorPoint = newDisplayEvent.anchorPoint if (anchorPoint == "BOTTOMLEFT") then newDisplayEvent.positionX = 0 elseif (anchorPoint == "BOTTOM") then newDisplayEvent.positionX = newDisplayEvent.scrollWidth / 2 elseif (anchorPoint == "BOTTOMRIGHT") then newDisplayEvent.positionX = newDisplayEvent.scrollWidth end newDisplayEvent.positionY = newDisplayEvent.scrollHeight / 2 -- Save the original x and y positions for calculating the jiggle effect. newDisplayEvent.originalPositionX = newDisplayEvent.positionX newDisplayEvent.originalPositionY = newDisplayEvent.positionY newDisplayEvent.timeLastJiggled = 0 -- Get the number of sticky display events that are currently animating. local numActiveAnimations = #activeDisplayEvents -- Exit if there is no need to check for collisions. if (numActiveAnimations == 0) then return end -- Check if the text is scrolling down. if (direction == "Down") then -- Get the middle sticky. local middleSticky = math_floor((numActiveAnimations + 2) / 2) -- Set the middle sticky to the center of the scroll area. activeDisplayEvents[middleSticky].originalPositionY = newDisplayEvent.scrollHeight / 2 activeDisplayEvents[middleSticky].positionY = activeDisplayEvents[middleSticky].originalPositionY -- Loop backwards from the middle sticky and move the animating display events so they don't collide. for x = middleSticky - 1, 1, -1 do activeDisplayEvents[x].originalPositionY = activeDisplayEvents[x+1].originalPositionY - activeDisplayEvents[x].fontSize - MIN_VERTICAL_SPACING activeDisplayEvents[x].positionY = activeDisplayEvents[x].originalPositionY end -- Loop forwards from the middle sticky and move the animating display events so they don't collide. for x = middleSticky + 1, numActiveAnimations do activeDisplayEvents[x].originalPositionY = activeDisplayEvents[x-1].originalPositionY + activeDisplayEvents[x-1].fontSize + MIN_VERTICAL_SPACING activeDisplayEvents[x].positionY = activeDisplayEvents[x].originalPositionY end -- Move the new display event so it doesn't collide. newDisplayEvent.originalPositionY = activeDisplayEvents[numActiveAnimations].originalPositionY + activeDisplayEvents[numActiveAnimations].fontSize + MIN_VERTICAL_SPACING newDisplayEvent.positionY = newDisplayEvent.originalPositionY -- Text is scrolling up. else -- Get the middle sticky. local middleSticky = math_ceil(numActiveAnimations / 2) -- Set the middle sticky to the center of the scroll area. activeDisplayEvents[middleSticky].originalPositionY = newDisplayEvent.scrollHeight / 2 activeDisplayEvents[middleSticky].positionY = activeDisplayEvents[middleSticky].originalPositionY -- Loop backwards from the middle sticky and move the animating display events so they don't collide. for x = middleSticky - 1, 1, -1 do activeDisplayEvents[x].originalPositionY = activeDisplayEvents[x+1].originalPositionY + activeDisplayEvents[x+1].fontSize + MIN_VERTICAL_SPACING activeDisplayEvents[x].positionY = activeDisplayEvents[x].originalPositionY end -- Loop forwards from the middle sticky and move the animating display events so they don't collide. for x = middleSticky + 1, numActiveAnimations do activeDisplayEvents[x].originalPositionY = activeDisplayEvents[x-1].originalPositionY - activeDisplayEvents[x].fontSize - MIN_VERTICAL_SPACING activeDisplayEvents[x].positionY = activeDisplayEvents[x].originalPositionY end -- Move the new display event so it doesn't collide. newDisplayEvent.originalPositionY = activeDisplayEvents[numActiveAnimations].originalPositionY - activeDisplayEvents[numActiveAnimations].fontSize - MIN_VERTICAL_SPACING newDisplayEvent.positionY = newDisplayEvent.originalPositionY end end ------------------------------------------------------------------------------- -- Angled scroll functions. ------------------------------------------------------------------------------- -- **************************************************************************** -- Scrolls the passed display event angled to the left and upwards. -- **************************************************************************** local function ScrollLeftAngledUp(displayEvent, animationProgress) local linePhasePercent = displayEvent.linePhasePercent local horizontalPhasePercent = displayEvent.horizontalPhasePercent -- Move the event in an angled line. if (animationProgress <= linePhasePercent) then -- Calculate how far along the current phase is and set the x and y positions accordingly. local phaseProgress = animationProgress / linePhasePercent displayEvent.positionX = displayEvent.scrollWidth - (displayEvent.startPositionX + (displayEvent.finishPositionX - displayEvent.startPositionX) * phaseProgress) displayEvent.positionY = displayEvent.finishPositionY * phaseProgress -- Wait a bit at the finish position. elseif (animationProgress <= horizontalPhasePercent) then displayEvent.positionX = displayEvent.scrollWidth - displayEvent.finishPositionX displayEvent.positionY = displayEvent.finishPositionY -- Move the event horizontally to outer edge. else -- Calculate how far along the current phase is and set the x and y positions accordingly. local phaseProgress = (animationProgress - horizontalPhasePercent) / (1 - horizontalPhasePercent) displayEvent.positionX = displayEvent.scrollWidth - (displayEvent.finishPositionX + ((displayEvent.scrollWidth - displayEvent.finishPositionX) * phaseProgress)) end end -- **************************************************************************** -- Scrolls the passed display event angled to the left and downwards. -- **************************************************************************** local function ScrollLeftAngledDown(displayEvent, animationProgress) local linePhasePercent = displayEvent.linePhasePercent local horizontalPhasePercent = displayEvent.horizontalPhasePercent -- Move the event in an angled line. if (animationProgress <= linePhasePercent) then -- Calculate how far along the current phase is and set the x and y positions accordingly. local phaseProgress = animationProgress / linePhasePercent displayEvent.positionX = displayEvent.scrollWidth - (displayEvent.startPositionX + (displayEvent.finishPositionX - displayEvent.startPositionX) * phaseProgress) displayEvent.positionY = displayEvent.scrollHeight - displayEvent.finishPositionY * phaseProgress -- Wait a bit at the finish position. elseif (animationProgress <= horizontalPhasePercent) then displayEvent.positionX = displayEvent.scrollWidth - displayEvent.finishPositionX displayEvent.positionY = displayEvent.scrollHeight - displayEvent.finishPositionY -- Move the event horizontally to outer edge. else -- Calculate how far along the current phase is and set the x and y positions accordingly. local phaseProgress = (animationProgress - horizontalPhasePercent) / (1 - horizontalPhasePercent) displayEvent.positionX = displayEvent.scrollWidth - (displayEvent.finishPositionX + ((displayEvent.scrollWidth - displayEvent.finishPositionX) * phaseProgress)) end end -- **************************************************************************** -- Scrolls the passed display event angled to the right and upwards. -- **************************************************************************** local function ScrollRightAngledUp(displayEvent, animationProgress) local linePhasePercent = displayEvent.linePhasePercent local horizontalPhasePercent = displayEvent.horizontalPhasePercent -- Move the event in an angled line. if (animationProgress <= linePhasePercent) then -- Calculate how far along the current phase is and set the x and y positions accordingly. local phaseProgress = animationProgress / linePhasePercent displayEvent.positionX = displayEvent.startPositionX + (displayEvent.finishPositionX - displayEvent.startPositionX) * phaseProgress displayEvent.positionY = displayEvent.finishPositionY * phaseProgress -- Wait a bit at the finish position. elseif (animationProgress <= horizontalPhasePercent) then displayEvent.positionX = displayEvent.finishPositionX displayEvent.positionY = displayEvent.finishPositionY -- Move the event horizontally to outer edge. else -- Calculate how far along the current phase is and set the x and y positions accordingly. local phaseProgress = (animationProgress - horizontalPhasePercent) / (1 - horizontalPhasePercent) displayEvent.positionX = displayEvent.finishPositionX + ((displayEvent.scrollWidth - displayEvent.finishPositionX) * phaseProgress) end end -- **************************************************************************** -- Scrolls the passed display event angled to the right and downwards. -- **************************************************************************** local function ScrollRightAngledDown(displayEvent, animationProgress) local linePhasePercent = displayEvent.linePhasePercent local horizontalPhasePercent = displayEvent.horizontalPhasePercent -- Move the event in an angled line. if (animationProgress <= linePhasePercent) then -- Calculate how far along the current phase is and set the x and y positions accordingly. local phaseProgress = animationProgress / linePhasePercent displayEvent.positionX = displayEvent.startPositionX + (displayEvent.finishPositionX - displayEvent.startPositionX) * phaseProgress displayEvent.positionY = displayEvent.scrollHeight - displayEvent.finishPositionY * phaseProgress -- Wait a bit at the finish position. elseif (animationProgress <= horizontalPhasePercent) then displayEvent.positionX = displayEvent.finishPositionX displayEvent.positionY = displayEvent.scrollHeight - displayEvent.finishPositionY -- Move the event horizontally to outer edge. else -- Calculate how far along the current phase is and set the x and y positions accordingly. local phaseProgress = (animationProgress - horizontalPhasePercent) / (1 - horizontalPhasePercent) displayEvent.positionX = displayEvent.finishPositionX + ((displayEvent.scrollWidth - displayEvent.finishPositionX) * phaseProgress) end end -- **************************************************************************** -- Initialize the passed display event and reposition the ones that are -- currently animating in the scroll area to prevent overlaps. -- **************************************************************************** local function InitAngled(newDisplayEvent, activeDisplayEvents, direction, behavior) -- Modify the direction and anchor if the direction is alternating. local startPositionX = 0 local anchorPoint = newDisplayEvent.anchorPoint if (direction ~= "Left" and direction ~= "Right") then -- Select direction and anchor point based on the last event. direction = (lastAngledDirection[activeDisplayEvents] == "Left") and "Right" or "Left" lastAngledDirection[activeDisplayEvents] = direction anchorPoint = (direction == "Left") and "BOTTOMRIGHT" or "BOTTOMLEFT" newDisplayEvent.anchorPoint = anchorPoint -- Start at the scroll area's mid point. startPositionX = newDisplayEvent.scrollWidth / 2 end -- Choose correct animation function. if (direction == "Right") then newDisplayEvent.animationHandler = (behavior == "AngleDown") and ScrollRightAngledDown or ScrollRightAngledUp else newDisplayEvent.animationHandler = (behavior == "AngleDown") and ScrollLeftAngledDown or ScrollLeftAngledUp end -- Calculate the y finish position based on the last event. local finishPositionY finishPositionY = lastAngledFinishPositionY[activeDisplayEvents] or newDisplayEvent.scrollHeight finishPositionY = finishPositionY - newDisplayEvent.fontSize - MIN_VERTICAL_SPACING if (finishPositionY < 0) then finishPositionY = newDisplayEvent.scrollHeight end -- Calculate how long the animation should take based on the distance the text has to travel. local animationSpeed = newDisplayEvent.animationSpeed local finishPositionX = newDisplayEvent.scrollWidth * ANGLED_WIDTH_PERCENT local linePhaseTime = math_sqrt((finishPositionX - startPositionX) * (finishPositionX - startPositionX) + finishPositionY * finishPositionY) * MOVEMENT_SPEED local scrollTime = ((linePhaseTime + ANGLED_HORIZONTAL_PHASE_TIME) / animationSpeed) + ANGLED_FADE_OUT_TIME newDisplayEvent.scrollTime = scrollTime * animationSpeed newDisplayEvent.linePhasePercent = linePhaseTime / animationSpeed / scrollTime newDisplayEvent.horizontalPhasePercent = ((linePhaseTime + ANGLED_HORIZONTAL_PHASE_TIME) / animationSpeed) / scrollTime newDisplayEvent.fadePercent = 1 - (ANGLED_FADE_OUT_TIME / scrollTime) -- Initialize the new event's x and y positions. newDisplayEvent.positionX = startPositionX newDisplayEvent.positionY = 0 newDisplayEvent.startPositionX = startPositionX newDisplayEvent.finishPositionX = newDisplayEvent.scrollWidth * ANGLED_WIDTH_PERCENT newDisplayEvent.finishPositionY = finishPositionY lastAngledFinishPositionY[activeDisplayEvents] = finishPositionY end ------------------------------------------------------------------------------- -- Straight scroll functions. ------------------------------------------------------------------------------- -- **************************************************************************** -- Scrolls the passed display event upwards. -- **************************************************************************** local function ScrollUp(displayEvent, animationProgress) -- Set the y position based on the percent completed. displayEvent.positionY = displayEvent.scrollHeight * animationProgress end -- **************************************************************************** -- Scrolls the passed display event downwards. -- **************************************************************************** local function ScrollDown(displayEvent, animationProgress) -- Set the y position based on the percent completed. displayEvent.positionY = displayEvent.scrollHeight - displayEvent.scrollHeight * animationProgress end -- **************************************************************************** -- Initialize the passed display event and reposition the ones that are -- currently scrolling in the scroll area to prevent overlaps. -- **************************************************************************** local function InitStraight(newDisplayEvent, activeDisplayEvents, direction, behavior) -- Calculate how long the animation should take based on the height of the scroll area. newDisplayEvent.scrollTime = newDisplayEvent.scrollHeight * MOVEMENT_SPEED -- Set the new event's starting X position. local anchorPoint = newDisplayEvent.anchorPoint if (anchorPoint == "BOTTOMLEFT") then newDisplayEvent.positionX = 0 elseif (anchorPoint == "BOTTOM") then newDisplayEvent.positionX = newDisplayEvent.scrollWidth / 2 elseif (anchorPoint == "BOTTOMRIGHT") then newDisplayEvent.positionX = newDisplayEvent.scrollWidth end -- Get the number of display events that are currently scrolling for this style. local numActiveAnimations = #activeDisplayEvents -- Scroll text down. if (direction == "Down") then -- Choose the correct animation function. newDisplayEvent.animationHandler = ScrollDown -- Exit if there is no need to check for collisions. if (numActiveAnimations == 0) then return end -- Scale the per pixel time based on the animation speed. local perPixelTime = MOVEMENT_SPEED / newDisplayEvent.animationSpeed local currentDisplayEvent = newDisplayEvent local prevDisplayEvent, topTimeCurrent -- Move events that are colliding. for x = numActiveAnimations, 1, -1 do prevDisplayEvent = activeDisplayEvents[x] -- Calculate the elapsed time for the top point of the current display event. topTimeCurrent = currentDisplayEvent.elapsedTime + (currentDisplayEvent.fontSize + MIN_VERTICAL_SPACING) * perPixelTime -- Adjust the elapsed time of the previous display event if the current one is colliding with it. if (prevDisplayEvent.elapsedTime < topTimeCurrent) then prevDisplayEvent.elapsedTime = topTimeCurrent else -- Don't continue checking if there is no need. break end currentDisplayEvent = prevDisplayEvent end -- Scroll text up. else -- Choose the correct animation function. newDisplayEvent.animationHandler = ScrollUp -- Exit if there is no need to check for collisions. if (numActiveAnimations == 0) then return end -- Scale the per pixel time based on the animation speed. local perPixelTime = MOVEMENT_SPEED / newDisplayEvent.animationSpeed local currentDisplayEvent = newDisplayEvent local prevDisplayEvent, topTimePrev -- Move events that are colliding. for x = numActiveAnimations, 1, -1 do prevDisplayEvent = activeDisplayEvents[x] -- Calculate the elapsed time for the top point of the previous display event. topTimePrev = prevDisplayEvent.elapsedTime - (prevDisplayEvent.fontSize + MIN_VERTICAL_SPACING) * perPixelTime -- Adjust the elapsed time of the previous display event if the current one is colliding with it. if (topTimePrev < currentDisplayEvent.elapsedTime) then prevDisplayEvent.elapsedTime = currentDisplayEvent.elapsedTime + (prevDisplayEvent.fontSize + MIN_VERTICAL_SPACING) * perPixelTime else -- Exit if there is no need to continue checking for collisions. return end currentDisplayEvent = prevDisplayEvent end end -- Direction. end ------------------------------------------------------------------------------- -- Parabola scroll functions. ------------------------------------------------------------------------------- -- **************************************************************************** -- Scrolls the passed display event in a left parabola upwards. -- **************************************************************************** local function ScrollLeftParabolaUp(displayEvent, animationProgress) -- Leverage the scroll up logic. ScrollUp(displayEvent, animationProgress) -- Calculate the new x position based on equation of a parabola. -- Equation of a parabola at vertex 0,0: x = y^2 / 4a local y = displayEvent.positionY - displayEvent.midPoint displayEvent.positionX = (y * y) / displayEvent.fourA end -- **************************************************************************** -- Scrolls the passed display event in a left parabola downwards. -- **************************************************************************** local function ScrollLeftParabolaDown(displayEvent, animationProgress) -- Leverage the scroll down logic. ScrollDown(displayEvent, animationProgress) -- Calculate the new x position based on equation of a parabola. -- Equation of a parabola at vertex 0,0: x = y^2 / 4a local y = displayEvent.positionY - displayEvent.midPoint displayEvent.positionX = (y * y) / displayEvent.fourA end -- **************************************************************************** -- Scrolls the passed display event in a right parabola upwards. -- **************************************************************************** local function ScrollRightParabolaUp(displayEvent, animationProgress) -- Leverage the scroll up logic. ScrollUp(displayEvent, animationProgress) -- Calculate the new x position based on equation of a parabola. -- Equation of a parabola at vertex 0,0: x = y^2 / 4a local y = displayEvent.positionY - displayEvent.midPoint displayEvent.positionX = displayEvent.scrollWidth - ((y * y) / displayEvent.fourA) end -- **************************************************************************** -- Scrolls the passed display event in a right parabola downwards. -- **************************************************************************** local function ScrollRightParabolaDown(displayEvent, animationProgress) -- Leverage the scroll down logic. ScrollDown(displayEvent, animationProgress) -- Calculate the new x position based on equation of a parabola. -- Equation of a parabola at vertex 0,0: x = y^2 / 4a local y = displayEvent.positionY - displayEvent.midPoint displayEvent.positionX = displayEvent.scrollWidth - ((y * y) / displayEvent.fourA) end -- **************************************************************************** -- Initialize the passed display event and reposition the ones that are -- currently scrolling in the scroll area to prevent overlaps. -- **************************************************************************** local function InitParabola(newDisplayEvent, activeDisplayEvents, direction, behavior) -- Leverage the straight logic. InitStraight(newDisplayEvent, activeDisplayEvents, direction, behavior) -- Choose correction animation function. if (direction == "Down") then newDisplayEvent.animationHandler = (behavior == "CurvedRight") and ScrollRightParabolaDown or ScrollLeftParabolaDown else newDisplayEvent.animationHandler = (behavior == "CurvedRight") and ScrollRightParabolaUp or ScrollLeftParabolaUp end -- Calculate the scroll area midpoint. local midPoint = newDisplayEvent.scrollHeight / 2 newDisplayEvent.midPoint = midPoint -- Calculate the parabola focal point. -- Equation of a parabola at vertex 0,0: x = y^2 / 4a newDisplayEvent.fourA = (midPoint * midPoint) / newDisplayEvent.scrollWidth end ------------------------------------------------------------------------------- -- Horizontal scroll functions. ------------------------------------------------------------------------------- -- **************************************************************************** -- Scrolls the passed display event horizontally left. -- **************************************************************************** local function ScrollLeft(displayEvent, animationProgress) -- Set the x position based on the percent completed.. displayEvent.positionX = displayEvent.scrollWidth - displayEvent.scrollWidth * animationProgress end -- **************************************************************************** -- Scrolls the passed display event horizontally right. -- **************************************************************************** local function ScrollRight(displayEvent, animationProgress) -- Set the x position based on the percent completed.. displayEvent.positionX = displayEvent.scrollWidth * animationProgress end -- **************************************************************************** -- Reposition the passed display events to prevent overlaps. -- **************************************************************************** local function RepositionHorizontalRight(currentDisplayEvent, activeDisplayEvents, startEvent) -- Scale the per pixel time based on the animation speed. local perPixelTime = MOVEMENT_SPEED / currentDisplayEvent.animationSpeed -- Get the top and bottom points of the current display events. local topCurrent = currentDisplayEvent.positionY + currentDisplayEvent.fontSize local bottomCurrent = currentDisplayEvent.positionY local prevDisplayEvent, topPrev, bottomPrev local leftTimePrev, rightTimeCurrent for x = startEvent, 1, -1 do -- Get the top and bottom points of the previous display event. prevDisplayEvent = activeDisplayEvents[x] topPrev = prevDisplayEvent.positionY + prevDisplayEvent.fontSize bottomPrev = prevDisplayEvent.positionY -- Check for a vertical collision. if ((topCurrent >= bottomPrev and topCurrent <= topPrev) or (bottomCurrent >= bottomPrev and bottomCurrent <= topPrev)) then -- Calculate the elapsed time for the left and right points. leftTimePrev = prevDisplayEvent.elapsedTime + (prevDisplayEvent.offsetLeft or 0) * perPixelTime rightTimeCurrent = currentDisplayEvent.elapsedTime + ((currentDisplayEvent.offsetRight or 0) + MIN_HORIZONTAL_SPACING) * perPixelTime -- Adjust the elapsed time of the previous display event if the current one is colliding with it. if (leftTimePrev <= rightTimeCurrent) then prevDisplayEvent.elapsedTime = rightTimeCurrent + math_abs((prevDisplayEvent.offsetLeft or 0) * perPixelTime) -- Move events that are now colliding as a result of moving this one. RepositionHorizontalRight(prevDisplayEvent, activeDisplayEvents, x - 1) end -- Horizontal collision. end -- Vertical collision. end end -- **************************************************************************** -- Reposition the passed display events to prevent overlaps. -- **************************************************************************** local function RepositionHorizontalLeft(currentDisplayEvent, activeDisplayEvents, startEvent) -- Scale the per pixel time based on the animation speed. local perPixelTime = MOVEMENT_SPEED / currentDisplayEvent.animationSpeed -- Get the top and bottom points of the current display events. local topCurrent = currentDisplayEvent.positionY + currentDisplayEvent.fontSize local bottomCurrent = currentDisplayEvent.positionY local prevDisplayEvent, topPrev, bottomPrev local rightTimePrev, leftTimeCurrent for x = startEvent, 1, -1 do -- Get the top and bottom points of the previous display event. prevDisplayEvent = activeDisplayEvents[x] topPrev = prevDisplayEvent.positionY + prevDisplayEvent.fontSize bottomPrev = prevDisplayEvent.positionY -- Check for a vertical collision. if ((topCurrent >= bottomPrev and topCurrent <= topPrev) or (bottomCurrent >= bottomPrev and bottomCurrent <= topPrev)) then -- Calculate the elapsed time for the left and right points. rightTimePrev = prevDisplayEvent.elapsedTime - ((prevDisplayEvent.offsetRight or 0) + MIN_HORIZONTAL_SPACING) * perPixelTime leftTimeCurrent = currentDisplayEvent.elapsedTime - (currentDisplayEvent.offsetLeft or 0) * perPixelTime -- Adjust the elapsed time of the previous display event if the current one is colliding with it. if (rightTimePrev <= leftTimeCurrent) then prevDisplayEvent.elapsedTime = leftTimeCurrent + ((prevDisplayEvent.offsetRight or 0) + MIN_HORIZONTAL_SPACING) * perPixelTime -- Move events that are now colliding as a result of moving this one. RepositionHorizontalLeft(prevDisplayEvent, activeDisplayEvents, x - 1) end -- Horizontal collision. end -- Vertical collision. end end -- **************************************************************************** -- Initialize the passed display event and reposition the ones that are -- currently scrolling in the scroll area to prevent overlaps. -- **************************************************************************** local function InitHorizontal(newDisplayEvent, activeDisplayEvents, direction, behavior) -- Calculate how long the animation should take based on the width of the scroll area. newDisplayEvent.scrollTime = newDisplayEvent.scrollWidth * MOVEMENT_SPEED -- Modify the direction and anchor if the direction is alternating. local anchorPoint = newDisplayEvent.anchorPoint if (direction ~= "Left" and direction ~= "Right") then -- Select direction and anchor point based on the last event. direction = (lastHorizontalDirection[activeDisplayEvents] == "Left") and "Right" or "Left" lastHorizontalDirection[activeDisplayEvents] = direction anchorPoint = (direction == "Left") and "BOTTOMRIGHT" or "BOTTOMLEFT" newDisplayEvent.anchorPoint = anchorPoint -- Start at the scroll area's mid point. newDisplayEvent.elapsedTime = newDisplayEvent.scrollTime / 2 end -- Calculate the left and right offsets from the anchor point. local fontStringWidth = newDisplayEvent.fontString:GetStringWidth() if (anchorPoint == "BOTTOMLEFT") then newDisplayEvent.offsetLeft = 0 newDisplayEvent.offsetRight = fontStringWidth elseif (anchorPoint == "BOTTOM") then local halfWidth = fontStringWidth / 2 newDisplayEvent.offsetLeft = -halfWidth newDisplayEvent.offsetRight = halfWidth elseif (anchorPoint == "BOTTOMRIGHT") then newDisplayEvent.offsetLeft = -fontStringWidth newDisplayEvent.offsetRight = 0 end -- Check if the text is growing down. local positionY if (behavior == "GrowDown") then -- Calculate the y position based on the last event. positionY = lastHorizontalPositionY[activeDisplayEvents] or newDisplayEvent.scrollHeight positionY = positionY - newDisplayEvent.fontSize - MIN_VERTICAL_SPACING if (positionY < 0) then positionY = newDisplayEvent.scrollHeight end -- Text is growing up. else -- Calculate the y position based on the last event. positionY = lastHorizontalPositionY[activeDisplayEvents] or 0 positionY = positionY + newDisplayEvent.fontSize + MIN_VERTICAL_SPACING if (positionY > newDisplayEvent.scrollHeight) then positionY = 0 end end -- Set the y position to the calculated value and save it for the next event. newDisplayEvent.positionY = positionY lastHorizontalPositionY[activeDisplayEvents] = positionY -- Get the number of display events that are currently scrolling for this style. local numActiveAnimations = #activeDisplayEvents -- Scroll text right. if (direction == "Right") then -- Choose the correct animation function. newDisplayEvent.animationHandler = ScrollRight -- Exit if there is no need to check for collisions. if (numActiveAnimations == 0) then return end -- Move events that are colliding. RepositionHorizontalRight(newDisplayEvent, activeDisplayEvents, numActiveAnimations) -- Scroll text left. else -- Choose the correct animation function. newDisplayEvent.animationHandler = ScrollLeft -- Exit if there is no need to check for collisions. if (numActiveAnimations == 0) then return end -- Move events that are colliding. RepositionHorizontalLeft(newDisplayEvent, activeDisplayEvents, numActiveAnimations) end end ------------------------------------------------------------------------------- -- Static scroll functions. ------------------------------------------------------------------------------- -- **************************************************************************** -- Animates the passed display event using the static style. -- **************************************************************************** local function ScrollStatic(displayEvent, animationProgress) -- Nothing needs to be done. end -- **************************************************************************** -- Initialize the passed display event and reposition the ones that are -- currently scrolling in the scroll area to prevent overlaps. -- **************************************************************************** local function InitStatic(newDisplayEvent, activeDisplayEvents, direction, behavior) -- Set how long the animation should take. newDisplayEvent.scrollTime = STATIC_DISPLAY_TIME -- Set the animation function. newDisplayEvent.animationHandler = ScrollStatic -- Set the new event's starting X position. local anchorPoint = newDisplayEvent.anchorPoint if (anchorPoint == "BOTTOMLEFT") then newDisplayEvent.positionX = 0 elseif (anchorPoint == "BOTTOM") then newDisplayEvent.positionX = newDisplayEvent.scrollWidth / 2 elseif (anchorPoint == "BOTTOMRIGHT") then newDisplayEvent.positionX = newDisplayEvent.scrollWidth end -- Get the number of display events that are currently animating for this style. local numActiveAnimations = #activeDisplayEvents local positionY -- Static display is growing downwards. if (direction == "Down") then positionY = newDisplayEvent.scrollHeight -- Offset the new display event correctly if there are already animating events. if (numActiveAnimations > 0) then -- Set the next y position to after the last display event. positionY = activeDisplayEvents[numActiveAnimations].positionY - newDisplayEvent.fontSize - MIN_VERTICAL_SPACING -- Wrap the y position if it is outside the scroll area's height. if (positionY < 0) then positionY = newDisplayEvent.scrollHeight end end -- Static display is growing upwards. else positionY = 0 -- Offset the new display event correctly if there are already animating events. if (numActiveAnimations > 0) then -- Set the next y position to before the last display event. positionY = activeDisplayEvents[numActiveAnimations].positionY + newDisplayEvent.fontSize + MIN_VERTICAL_SPACING -- Wrap the y position if it is outside the scroll area's height. if (positionY > newDisplayEvent.scrollHeight) then positionY = 0 end end end -- Check if there are already animating events. if (numActiveAnimations > 0) then -- Get the top and bottom points of the new display event. local topNew = positionY + newDisplayEvent.fontSize local bottomNew = positionY -- Loop through all the old display events to force old animations that the new one overlaps to complete. local oldDisplayEvent, topOld, bottomOld for x = 1, numActiveAnimations - 1 do -- Get the top and bottom points of the old display event. oldDisplayEvent = activeDisplayEvents[x] bottomOld = oldDisplayEvent.positionY topOld = bottomOld + oldDisplayEvent.fontSize -- Force the old animation to complete if the new display event is overlapping it. if ((topNew >= bottomOld and topNew <= topOld) or (bottomNew >= bottomOld and bottomNew <= topOld)) then oldDisplayEvent.elapsedTime = oldDisplayEvent.scrollTime end end end -- Set the y position to the calculated value. newDisplayEvent.positionY = positionY end ------------------------------------------------------------------------------- -- Initialization. ------------------------------------------------------------------------------- -- Register the default animation styles. MikSBT.RegisterAnimationStyle("Angled", InitAngled, "Alternate;Left;Right", "AngleUp;AngleDown") MikSBT.RegisterAnimationStyle("Straight", InitStraight, "Up;Down", nil) MikSBT.RegisterAnimationStyle("Parabola", InitParabola, "Up;Down", "CurvedLeft;CurvedRight") MikSBT.RegisterAnimationStyle("Horizontal", InitHorizontal, "Alternate;Left;Right", "GrowUp;GrowDown") MikSBT.RegisterAnimationStyle("Static", InitStatic, "Up;Down", nil) -- Register the default sticky animation styles. MikSBT.RegisterStickyAnimationStyle("Pow", InitPow, "Up;Down", "Normal;Jiggle") MikSBT.RegisterStickyAnimationStyle("Static", InitStatic, "Up;Down", nil)