myspace / Lua /Tactical /CombatLog.lua
sirnii's picture
Upload 1816 files
b6a38d7 verified
raw
history blame
17.6 kB
GameVar("gv_LogData", {})
if FirstLoad then
LogData = {}
LogShowDebug = false
CombatLogAnchorBox = false
CombatLogAnchoredBoxes = {}
end
function OnMsg.GatherSessionData()
for i, item in ipairs(LogData) do
gv_LogData[i] = {item[1], _InternalTranslate(item[2]), item[3]}
end
end
function OnMsg.LoadSessionData()
LogData = {}
for i, item in ipairs(gv_LogData) do
LogData[i] = {item[1], Untranslated(item[2]), item[3]}
end
local dlg = GetDialog("CombatLog")
if dlg then
dlg:UpdateText()
end
end
function OnMsg.NewGameSessionStart()
local dlg = GetDialog("CombatLog")
if dlg then
dlg:UpdateText()
end
end
function OnMsg.EnterSector(game_start, load_game)
if not (game_start or load_game) then
gv_LogData = {}
LogData = {}
local dlg = GetDialog("CombatLog")
if dlg then
dlg:UpdateText()
end
end
end
function InGameInterface:OnShortcut(shortcut, source, ...)
local desktop = self.desktop
if desktop:GetModalWindow() == desktop and self.mode_dialog and self.mode_dialog:GetVisible() and desktop.keyboard_focus and not desktop.keyboard_focus:IsWithin(self.mode_dialog) then
return self.mode_dialog:OnShortcut(shortcut, source, ...)
end
end
local function lResolveCombatLogMessageActor(prevLine, name)
-- Merge same name lines one after another
if prevLine and prevLine[1] == name then
return false
end
if name == "debug" then
return Untranslated("Debug")
elseif name == "helper" or name == "importanthelper" then
return "helper"
elseif name == "short" then
return T(502734170556, "AIMBot")
elseif UnitDataDefs[name] then
return UnitDataDefs[name].Nick or UnitDataDefs[name].Name or Untranslated(name)
elseif IsT(name) then
return name
else
return Untranslated(name)
end
end
DefineClass.CombatLogAnchorAnimationWindow = {
__parents = { "XDialog" },
properties = {
{ editor = "bool", id = "flip_vertically", default = false }
},
popup_time = 200,
suppressesCombatLog = false,
}
function CombatLogAnchorAnimationWindow:SuppressCombatLog()
local dlg = GetDialog("CombatLog")
if not dlg or dlg.window_state == "destroying" then return end
dlg.idLogContainer:SetVisible(false)
local fader = GetDialog("CombatLogMessageFader")
if fader then fader:SetVisible(false) end
self.suppressesCombatLog = true
end
function CombatLogAnchorAnimationWindow:OnDelete()
if not self.suppressesCombatLog then return end
local dlg = GetDialog("CombatLog")
if not dlg then return end
local fader = GetDialog("CombatLogMessageFader")
if fader then fader:SetVisible(true) end
dlg.idLogContainer:SetVisible(true)
end
function CombatLogAnchorAnimationWindow:Open()
-- Turn invisible if an anchor exists.
-- This will trigger the animation in OnLayoutComplete
CombatLogAnchoredBoxes[self] = true
self:SetVisible(not CombatLogAnchorBox)
XDialog.Open(self)
end
function CombatLogAnchorAnimationWindow:Close()
CombatLogAnchoredBoxes[self] = nil
XDialog.Close(self)
end
function OnMsg.CombatLogVisibleChanged(state)
if state == "start hiding" then
PlayFX("CombatLogClose", "start")
elseif state == "start showing" then
PlayFX("CombatLogOpen", "start")
end
end
function CombatLogAnchorAnimationWindow:AnimatedClose(hideInsteadOfClose, instant)
self:DeleteThread("animation-open")
if not self:IsVisible() then
self.open = false
return
end
if instant then
Msg("CombatLogVisibleChanged", "start hiding")
if hideInsteadOfClose then
self:SetVisible(false)
self.open = false
else
self:Close()
end
return
end
if self:GetThread("animation-close") then return end
self:CreateThread("animation-close", function()
self:AddInterpolation{
id = "size",
type = const.intRect,
duration = self.popup_time,
originalRect = self.box,
targetRect = CombatLogAnchorBox,
}
Msg("CombatLogVisibleChanged", "start hiding")
Sleep(self.popup_time)
if self.window_state ~= "open" then return end
if hideInsteadOfClose then
self:SetVisible(false)
self.open = false
else
self:Close()
end
end)
end
function CombatLogAnchorAnimationWindow:AnimatedOpen()
local combatLogFader = GetDialog("CombatLogMessageFader")
if combatLogFader then combatLogFader:DeleteChildren() end
self:DeleteThread("animation-close")
if self:GetThread("animation-open") then return end
self:CreateThread("animation-open", function()
Sleep(1)
if self.visible then return end
self:SetBoxFromAnchor()
self:SetVisible(true)
self:AddInterpolation{
id = "size",
type = const.intRect,
duration = self.popup_time,
originalRect = self.box,
targetRect = CombatLogAnchorBox,
flags = const.intfInverse
}
local isCombatLog = IsKindOf(self, "CombatLogWindow")
if isCombatLog then Msg("CombatLogVisibleChanged", "start showing") end
Sleep(self.popup_time)
if isCombatLog then Msg("CombatLogVisibleChanged", "visible") end
end)
end
function CombatLogAnchorAnimationWindow:OnLayoutComplete()
if not CombatLogAnchorBox or self:GetThread("animation-close") then return end
self:AnimatedOpen()
end
function CombatLogAnchorAnimationWindow:SetBoxFromAnchor()
local x, y, width, height = false, false, false, false
width, height = self.measure_width, self.measure_height
local heightLimitPoint = GetCombatLogHeightLimit()
local _, marginY = ScaleXY(self.scale, 0, 5)
if heightLimitPoint then
heightLimitPoint = heightLimitPoint - marginY
end
if self.flip_vertically then
if heightLimitPoint and CombatLogAnchorBox:miny() + height >= heightLimitPoint then
x, y = CombatLogAnchorBox:minx(), CombatLogAnchorBox:miny() - height + CombatLogAnchorBox:sizey()
else
x, y = CombatLogAnchorBox:minx(), CombatLogAnchorBox:miny()
end
else
local max = CombatLogAnchorBox:miny() + height
if heightLimitPoint and max >= heightLimitPoint then
local belowHeightPoint = max - heightLimitPoint
x, y = CombatLogAnchorBox:minx(), CombatLogAnchorBox:miny() - belowHeightPoint
else
x, y = CombatLogAnchorBox:minx(), CombatLogAnchorBox:miny()
end
end
if self.Dock == "ignore" then
self:SetBox(x, y, width, height, true)
elseif self.SetBoxFromAnchorInternal then
self:SetBoxFromAnchorInternal(x, y)
end
end
function OnMsg.CombatLogButtonChanged()
-- Ensure combat log is open.
if not GetDialog("CombatLog") then
local dlg = OpenDialog("CombatLog")
dlg:SetVisible(false)
end
-- Invalidate any UIs attached to this button.
for wnd, visible in pairs(CombatLogAnchoredBoxes) do
if wnd and wnd.window_state ~= "destroying" then
wnd:SetBoxFromAnchor()
end
end
-- Ensure combat log doesn't clip into anything.
local log = GetDialog("CombatLog")
log:SetBoxFromAnchor()
end
CombatLogDefaultZOrder = 2
table.insert(BlacklistedDialogClasses, "CombatLogMessageFader")
DefineClass.CombatLogMessageFader = {
__parents = { "XDialog" },
Dock = "ignore",
ZOrder = CombatLogDefaultZOrder,
LayoutMethod = "VList",
FocusOnOpen = "",
MinWidth = 500,
MaxWidth = 500,
Clip = "self",
HandleMouse = false,
ChildrenHandleMouse = false
}
local irInside = const.irInside
local Intersect2D = empty_box.Intersect2D
function CombatLogMessageFader:DrawChildren(clip_box)
local chidren_on_top
local UseClipBox = self.UseClipBox
for _, win in ipairs(self) do
if not win.visible or win.outside_parent then goto continue end
if win.DrawOnTop then
chidren_on_top = true
goto continue
end
local intersection = Intersect2D(self.content_box, win.box)
if intersection == irInside then
win:DrawWindow(clip_box)
end
::continue::
end
if chidren_on_top then
for _, win in ipairs(self) do
if win.DrawOnTop and win.visible and not win.outside_parent and (not UseClipBox or Intersect2D(win.box, clip_box) ~= irOutside) then
win:DrawWindow(clip_box)
end
end
end
end
function CombatLogMessageFader:UpdateLayout()
if not self.layout_update then return end
if GetUIStyleGamepad() then
local bottomBar
local verticalOffset
if not gv_SatelliteView then
local igi = GetInGameInterfaceModeDlg()
bottomBar = igi and igi.idBottomBar
verticalOffset = 25
else
bottomBar = g_SatTimelineUI
verticalOffset = -50
end
if bottomBar then
local _, yMargin = ScaleXY(self.scale, 0, verticalOffset)
local bbbbox = bottomBar.box
self:SetBox(
bbbbox:minx() + bbbbox:sizex() / 2 - self.measure_width / 2,
bbbbox:miny() - self.measure_height - yMargin,
self.measure_width,
self.measure_height
)
XDialog.UpdateLayout(self)
return
end
end
local heightLimitPoint = GetCombatLogHeightLimit()
local _, marginY = ScaleXY(self.scale, 0, 5)
if heightLimitPoint then
heightLimitPoint = heightLimitPoint - marginY
end
local x = CombatLogAnchorBox and CombatLogAnchorBox:minx() or 0
local y = CombatLogAnchorBox and CombatLogAnchorBox:maxy() or 0
local height = 0
for i, w in ipairs(self) do
height = height + w.measure_height
if i > 4 then break end
end
local yMax = y + height
if heightLimitPoint and yMax >= heightLimitPoint then
local belowHeightPoint = yMax - heightLimitPoint
y = y - belowHeightPoint
end
self:SetBox(x, y, self.measure_width, height)
XDialog.UpdateLayout(self)
end
DefineClass.CombatLogText = {
__parents = { "XText" },
Translated = true,
Padding = box(0, 0, 0, 0),
TextStyle = "CombatLog",
HAlign = "left",
VAlign = "top",
rendered_least_once = false
}
function CombatLogText:DrawWindow(...)
self.rendered_least_once = true
return XWindow.DrawWindow(self, ...)
end
DefineClass.CombatLogWindow = {
__parents = { "CombatLogAnchorAnimationWindow" },
Dock = "ignore",
ZOrder = CombatLogDefaultZOrder,
scroll_area = false,
main_textbox = false,
FocusOnOpen = "",
open = false
}
function CombatLogWindow:Open(...)
CombatLogAnchorAnimationWindow.Open(self, ...)
self.scroll_area = self:ResolveId("idScrollArea")
self:UpdateText()
local satellite = GetDialog("PDADialogSatellite")
if satellite and satellite.window_state ~= "destroying" then
self:SetZOrder(1)
local popupHost = satellite:ResolveId("idDisplayPopupHost")
self:SetParent(popupHost)
else
self:SetZOrder(CombatLogDefaultZOrder)
self:SetParent(GetInGameInterface())
end
end
function CombatLogWindow:AnimatedOpen()
if self.open then return end
local satellite = GetDialog("PDADialogSatellite")
if satellite and not self:IsWithin(satellite) and satellite.window_state ~= "destroying" then
local popupHost = satellite:ResolveId("idDisplayPopupHost")
self:SetZOrder(1)
self:SetParent(popupHost)
self:UpdateMeasure(self.last_max_width, self.last_max_height)
end
CombatLogAnchorAnimationWindow.AnimatedOpen(self)
self:ScrollToBottom()
self.open = true
end
function CombatLogWindow:OnDelete()
Msg("CombatLogVisibleChanged")
end
function CombatLogWindow:OnLayoutComplete(...)
if self.visible then -- The OnLayoutComplete animation can cause this window to appear when hidden.
CombatLogAnchorAnimationWindow.OnLayoutComplete(self)
end
end
function CombatLogWindow:ScrollToBottom()
self:DeleteThread("delayed_scroll")
self:CreateThread("delayed_scroll", function()
if #self.scroll_area == 0 then return end
local lastLine = self.scroll_area[#self.scroll_area]
self.scroll_area:ScrollIntoView(lastLine)
end)
end
function CombatLogWindow:OnScaleChanged()
self:ScrollToBottom()
end
function GetCombatLogHeightLimit()
if g_SatelliteUI then
local satDiag = GetDialog(g_SatelliteUI)
local startButton = satDiag and satDiag.idStartButton
if startButton then
return startButton.box:miny()
end
end
local pda = GetDialog("PDADialogSatellite")
if pda then
return pda.idDisplay.box:maxy()
end
local igi = GetInGameInterfaceModeDlg()
if not IsKindOf(igi, "IModeCommonUnitControl") then return end
if igi and igi.idStartButton then
return igi.idStartButton.box:miny()
end
local bottomLeftUI = igi.idLeft
if not bottomLeftUI then return end
local limitPoint = bottomLeftUI.box:miny()
local weaponUI = igi.idWeaponUI
if weaponUI and weaponUI.idOtherSets then
--limitPoint = weaponUI.idOtherSets.box:miny()
end
return limitPoint
end
local function FindLastFilterPassingLine()
for i = #LogData - 1, 1, -1 do
local item = LogData[i]
if (item[1]~= "helper" or item[1]~= "importanthelper") and (item[1] ~= "debug" or LogShowDebug) then
return item
end
end
end
local function lSetupText(textWnd, item, name)
textWnd:SetTranslate(true)
if name then
if name == "helper" or name == "importanthelper" then
textWnd:SetText(T{997455386796, "<indent><text>", indent = " ", text = item[2]})
else
textWnd:SetText(T{632558295987, "<em><name></em>: <text>", name = name, text = item[2]})
end
else
textWnd:SetText(T{588344253198, "><text>", text = item[2]})
end
if textWnd.window_state ~= "open" then textWnd:Open() end
end
function CombatLogWindow:LineAdded(item)
if item[1] == "debug" and not LogShowDebug then return end
local isAtBottom = (self.scroll_area.scroll_range_y - self.scroll_area.content_box:sizey()) - self.scroll_area.PendingOffsetY < self.scroll_area.MouseWheelStep
local newLabel = XTemplateSpawn("CombatLogText", self.scroll_area)
lSetupText(newLabel, item, lResolveCombatLogMessageActor(FindLastFilterPassingLine(), item[1]))
if isAtBottom then
self.scroll_area:ScrollIntoView(newLabel)
end
end
function CombatLogWindow:UpdateText()
local idScrollArea = self.scroll_area
local spawnedLines = #idScrollArea
local textLines = #LogData
local count = 0
local prevFilteredLine = false
for i, item in ipairs(LogData) do
local name = item[1]
if name ~= "debug" or LogShowDebug then
count = count + 1
local labelWindow
local nameResolved = lResolveCombatLogMessageActor(prevFilteredLine, name)
if count > spawnedLines then
labelWindow = XTemplateSpawn("CombatLogText", idScrollArea)
else
labelWindow = idScrollArea[count]
end
lSetupText(labelWindow, item, nameResolved)
prevFilteredLine = item
end
end
while #idScrollArea > count do
idScrollArea[#idScrollArea]:delete()
end
if #idScrollArea > 0 then
idScrollArea:ScrollIntoView(idScrollArea[#idScrollArea])
end
end
function CombatLogWindow:OnMouseButtonDown(pt, ...)
if not self:MouseInWindow(pt) then return end
XDialog.OnMouseButtonDown(self, pt, ...)
return "break"
end
function CombatLogWindow:OnMouseButtonUp(pt, ...)
if not self:MouseInWindow(pt) then return end
XDialog.OnMouseButtonUp(self, pt, ...)
return "break"
end
function CombatLogWindow:OnMouseWheelForward(pt, ...)
if not self:MouseInWindow(pt) then return end
local scroll = self:ResolveId("idScrollArea")
scroll:OnMouseWheelForward(pt, ...)
return "break"
end
function CombatLogWindow:OnMouseWheelBack(pt, ...)
if not self:MouseInWindow(pt) then return end
local scroll = self:ResolveId("idScrollArea")
scroll:OnMouseWheelBack(pt, ...)
return "break"
end
function CombatLog(actor, msg, dontTHN)
if CheatEnabled("CombatUIHidden") then return end
if actor == "debug" and not IsT(msg) then
msg = Untranslated(msg)
end
assert(msg and IsT(msg))
local important = actor == "important" or actor == "importanthelper"
if important then actor = "short" end
local newLine = { actor, msg, Game and Game.CampaignTime or 0}
LogData[#LogData + 1] = newLine
local diag = GetDialog("CombatLog")
if diag then diag:LineAdded(newLine) end
ObjModified(LogData)
if important and (not diag or not diag.open) then
local nameResolved = lResolveCombatLogMessageActor(false, actor)
local faderContainer = GetDialog("CombatLogMessageFader") or OpenDialog("CombatLogMessageFader")
local labelWindow = XTemplateSpawn("CombatLogText", faderContainer)
labelWindow:SetTextStyle("CombatLogFade")
labelWindow:SetBackground(GetColorWithAlpha(GameColors.DarkB, 125))
labelWindow:SetPadding(box(5, 5, 5, 5))
labelWindow:SetMinWidth(faderContainer.MinWidth)
labelWindow:SetMaxWidth(faderContainer.MaxWidth)
-- Hide fading texts while in a conversation
labelWindow:SetVisible(false, true)
labelWindow:CreateThread(function()
WaitPlayerControl()
RunWhenXWindowIsReady(labelWindow, function()
labelWindow:AddInterpolation({
id = "move",
type = const.intRect,
OnLayoutComplete = IntRectTopLeftRelative,
targetRect = labelWindow:CalcZoomedBox(600),
originalRect = labelWindow.box,
duration = 200,
autoremove = true,
flags = const.intfInverse
})
end)
labelWindow:SetVisible(true, true)
while not labelWindow.rendered_least_once do
Sleep(5)
end
Sleep(10000)
labelWindow.FadeOutTime = 400
labelWindow:SetVisible(false)
Sleep(labelWindow.FadeOutTime)
labelWindow:Close()
end)
lSetupText(labelWindow, newLine, nameResolved)
end
end
function HideCombatLog(nonInstant)
local combatLog = GetDialog("CombatLog")
if combatLog then
combatLog:AnimatedClose("hideInsteadOfClose", not nonInstant and "instant")
local fader = GetDialog("CombatLogMessageFader")
if fader then fader:DeleteChildren() end
end
end
OnMsg.OpenPDA = HideCombatLog
OnMsg.CloseSatelliteView = HideCombatLog
OnMsg.ModifyWeaponDialogOpened = HideCombatLog
OnMsg.HideCombatLog = HideCombatLog
function GetCombatLogNameStyle(unitTemplate)
local affil = unitTemplate and unitTemplate.Affiliation
if affil == "AIM" then
return "CombatLogNameMerc"
elseif affil == "Legion" then
return "CombatLogNameEnemy"
end
return "CombatLogButtonActive"
end
function OpenCombatLog()
if CheatEnabled("CombatUIHidden") then return end
local combatLog = GetDialog("CombatLog")
if combatLog then
combatLog:AnimatedOpen()
return
end
OpenDialog("CombatLog")
end