148 | function HUDPopupMessage:assignCurrentMessage(message) |
149 | self.time = 0 |
150 | self.currentMessage = message |
151 | |
152 | local reqHeight = self:getTitleHeight() + self:getTextHeight() + self:getInputRowsHeight() |
153 | reqHeight = reqHeight + self.borderPaddingY * 2 + self.textOffsetY + self.titleTextSize + self.textSize |
154 | if #message.controls > 0 then |
155 | reqHeight = reqHeight + self.inputRowsOffsetY |
156 | end |
157 | |
158 | if not g_isServerStreamingVersion then |
159 | reqHeight = reqHeight + self.skipButtonHeight |
160 | end |
161 | |
162 | self:setDimension(self:getWidth(), math.max(self.minHeight, reqHeight)) |
163 | self:updateButtonGlyphs() |
164 | end |
481 | function HUDPopupMessage:createComponents(hudAtlasPath) |
482 | local basePosX, basePosY = self:getPosition() |
483 | local baseWidth = self:getWidth() |
484 | |
485 | local _, inputRowHeight = self:scalePixelToScreenVector(HUDPopupMessage.SIZE.INPUT_ROW) |
486 | |
487 | local posY = basePosY + inputRowHeight -- add one row's height as spacing for the skip button |
488 | for i = 1, HUDPopupMessage.MAX_INPUT_ROW_COUNT do |
489 | local buttonRow, inputGlyph |
490 | |
491 | buttonRow, inputGlyph, posY = self:createInputRow(hudAtlasPath, basePosX, posY) |
492 | local rowIndex = HUDPopupMessage.MAX_INPUT_ROW_COUNT - i + 1 |
493 | self.inputRows[rowIndex] = buttonRow |
494 | self.inputGlyphs[rowIndex] = inputGlyph |
495 | self:addChild(buttonRow) |
496 | end |
497 | |
498 | if not g_isServerStreamingVersion then |
499 | local offX, offY = self:scalePixelToScreenVector(HUDPopupMessage.POSITION.SKIP_BUTTON) |
500 | local glyphWidth, glyphHeight = self:scalePixelToScreenVector(HUDPopupMessage.SIZE.INPUT_GLYPH) |
501 | local skipGlyph = InputGlyphElement.new(self.inputDisplayManager, glyphWidth, glyphHeight) |
502 | skipGlyph:setPosition(basePosX + (baseWidth - glyphWidth) * 0.5 + offX, basePosY - offY) |
503 | skipGlyph:setAction(InputAction.SKIP_MESSAGE_BOX, self.l10n:getText(HUDPopupMessage.L10N_SYMBOL.BUTTON_OK), self.skipTextSize, true, false) |
504 | |
505 | self.skipGlyph = skipGlyph |
506 | self:addChild(skipGlyph) |
507 | end |
508 | end |
512 | function HUDPopupMessage:createInputRow(hudAtlasPath, posX, posY) |
513 | local overlay = Overlay.new(hudAtlasPath, posX, posY, self.inputRowWidth, self.inputRowHeight) |
514 | overlay:setUVs(GuiUtils.getUVs(HUDPopupMessage.UV.BACKGROUND)) |
515 | overlay:setColor(unpack(HUDPopupMessage.COLOR.INPUT_ROW)) |
516 | local buttonPanel = HUDElement.new(overlay) |
517 | |
518 | local rowHeight = buttonPanel:getHeight() |
519 | |
520 | local glyphWidth, glyphHeight = self:scalePixelToScreenVector(HUDPopupMessage.SIZE.INPUT_GLYPH) |
521 | local inputGlyph = InputGlyphElement.new(self.inputDisplayManager, glyphWidth, glyphHeight) |
522 | local offX, offY = self:scalePixelToScreenVector(HUDPopupMessage.POSITION.INPUT_GLYPH) |
523 | local glyphX, glyphY = posX + self.borderPaddingX + offX, posY + (rowHeight - glyphHeight) * 0.5 + offY |
524 | inputGlyph:setPosition(glyphX, glyphY) |
525 | buttonPanel:addChild(inputGlyph) |
526 | |
527 | local width, height = self:scalePixelToScreenVector(HUDPopupMessage.SIZE.SEPARATOR) |
528 | height = math.max(height, HUDPopupMessage.SIZE.SEPARATOR[2] / g_screenHeight) |
529 | offX, offY = self:scalePixelToScreenVector(HUDPopupMessage.POSITION.SEPARATOR) |
530 | overlay = Overlay.new(hudAtlasPath, posX + offX, posY + offY, width, height) |
531 | overlay:setUVs(GuiUtils.getUVs(GameInfoDisplay.UV.SEPARATOR)) |
532 | overlay:setColor(unpack(GameInfoDisplay.COLOR.SEPARATOR)) |
533 | local separator = HUDElement.new(overlay) |
534 | buttonPanel:addChild(separator) |
535 | |
536 | return buttonPanel, inputGlyph, posY + rowHeight |
537 | end |
366 | function HUDPopupMessage:draw() |
367 | if not self.isMenuVisible and self:getVisible() and self.currentMessage ~= nil then |
368 | HUDPopupMessage:superClass().draw(self) |
369 | |
370 | local baseX, baseY = self:getPosition() |
371 | local width, height = self:getWidth(), self:getHeight() |
372 | |
373 | -- title |
374 | setTextColor(unpack(HUDPopupMessage.COLOR.TITLE)) |
375 | setTextBold(true) |
376 | setTextAlignment(RenderText.ALIGN_CENTER) |
377 | setTextWrapWidth(width - 2 * self.borderPaddingX) |
378 | local textPosY = baseY + height - self.borderPaddingY |
379 | |
380 | if self.currentMessage.title ~= "" then |
381 | local title = utf8ToUpper(self.currentMessage.title) |
382 | textPosY = textPosY - self.titleTextSize |
383 | renderText(baseX + width * 0.5, textPosY, self.titleTextSize, title) |
384 | end |
385 | |
386 | -- message |
387 | setTextBold(false) |
388 | setTextColor(unpack(HUDPopupMessage.COLOR.TEXT)) |
389 | setTextAlignment(RenderText.ALIGN_LEFT) |
390 | setTextLineHeightScale(HUDPopupMessage.TEXT_LINE_HEIGHT_SCALE) |
391 | textPosY = textPosY - self.textSize + self.textOffsetY |
392 | renderText(baseX + self.borderPaddingX, textPosY, self.textSize, self.currentMessage.message) |
393 | textPosY = textPosY - getTextHeight(self.textSize, self.currentMessage.message) |
394 | |
395 | -- input rows |
396 | setTextColor(unpack(HUDPopupMessage.COLOR.SKIP_TEXT)) |
397 | setTextAlignment(RenderText.ALIGN_RIGHT) |
398 | local posX = baseX + width - self.borderPaddingX |
399 | local posY = textPosY + self.inputRowsOffsetY - self.inputRowHeight - self.textSize |
400 | for i = 1, #self.currentMessage.controls do |
401 | local inputText = self.currentMessage.controls[i].textRight |
402 | renderText(posX + self.inputRowTextX, posY + self.inputRowTextY, self.textSize, inputText) |
403 | |
404 | posY = posY - self.inputRowHeight |
405 | end |
406 | |
407 | -- reset uncommon text settings: |
408 | setTextWrapWidth(0) |
409 | setTextLineHeightScale(RenderText.DEFAULT_LINE_HEIGHT_SCALE) |
410 | end |
411 | end |
41 | function HUDPopupMessage.new(hudAtlasPath, l10n, inputManager, inputDisplayManager, ingameMap, guiSoundPlayer) |
42 | local backgroundOverlay = HUDPopupMessage.createBackground(hudAtlasPath) |
43 | local self = HUDPopupMessage:superClass().new(backgroundOverlay, nil, HUDPopupMessage_mt) |
44 | |
45 | self.l10n = l10n |
46 | self.inputManager = inputManager |
47 | self.inputDisplayManager = inputDisplayManager |
48 | self.ingameMap = ingameMap -- in game map reference required to hide the map when showing a message |
49 | self.guiSoundPlayer = guiSoundPlayer |
50 | |
51 | self.pendingMessages = {} -- {i={<message as defined in showMessage()>}}, ordered as a queue |
52 | self.isCustomInputActive = false -- input state flag |
53 | self.lastInputMode = self.inputManager:getInputHelpMode() |
54 | |
55 | self.inputRows = {} -- {i=HUDElement} |
56 | self.inputGlyphs = {} -- {i=InputGlyphElement}, synchronous with self.inputRows |
57 | self.skipGlyph = nil -- InputGlyphElement |
58 | |
59 | self.isMenuVisible = false |
60 | self.time = 0 -- accumulated message display time |
61 | self.isGamePaused = false -- game paused state |
62 | |
63 | self:storeScaledValues() |
64 | self:createComponents(hudAtlasPath) |
65 | |
66 | return self |
67 | end |
329 | function HUDPopupMessage:setInputActive(isActive) |
330 | if not self.isCustomInputActive and isActive then |
331 | self.inputManager:setContext(HUDPopupMessage.INPUT_CONTEXT_NAME, true, false) |
332 | |
333 | local _, eventId = self.inputManager:registerActionEvent(InputAction.MENU_ACCEPT, self, self.onConfirmMessage, false, true, false, true) |
334 | self.inputManager:setActionEventTextVisibility(eventId, false) |
335 | |
336 | _, eventId = self.inputManager:registerActionEvent(InputAction.SKIP_MESSAGE_BOX, self, self.onConfirmMessage, false, true, false, true) |
337 | self.inputManager:setActionEventTextVisibility(eventId, false) |
338 | |
339 | self.isCustomInputActive = true |
340 | elseif self.isCustomInputActive and not isActive then |
341 | self.inputManager:removeActionEventsByTarget(self) |
342 | self.inputManager:revertContext(true) -- revert and clear message context |
343 | self.isCustomInputActive = false |
344 | end |
345 | end |
88 | function HUDPopupMessage:showMessage(title, text, duration, controls, callback, target) |
89 | if duration == 0 then -- if no duration indicated, adjust duration according to message length |
90 | duration = HUDPopupMessage.MIN_DURATION + string.len(text) * HUDPopupMessage.DURATION_PER_CHARACTER |
91 | elseif duration < 0 then -- a negative duration is adjusted to five minutes ("almost" indefinite) |
92 | duration = HUDPopupMessage.MAX_DURATION |
93 | end |
94 | |
95 | while #self.pendingMessages > HUDPopupMessage.MAX_PENDING_MESSAGE_COUNT do |
96 | table.remove(self.pendingMessages, 1) |
97 | end |
98 | |
99 | local message = { |
100 | isDialog=false, |
101 | title=title, |
102 | message=text, |
103 | duration=duration, |
104 | controls=Utils.getNoNil(controls, {}), |
105 | callback=callback, |
106 | target=target |
107 | } |
108 | |
109 | if #message.controls > HUDPopupMessage.MAX_INPUT_ROW_COUNT then -- truncate |
110 | for i = #message.controls, HUDPopupMessage.MAX_INPUT_ROW_COUNT + 1, -1 do |
111 | table.remove(message.controls, i) |
112 | end |
113 | end |
114 | |
115 | table.insert(self.pendingMessages, message) |
116 | end |
445 | function HUDPopupMessage:storeScaledValues() |
446 | self.minWidth, self.minHeight = self:scalePixelToScreenVector(HUDPopupMessage.SIZE.SELF) |
447 | |
448 | self.textOffsetX, self.textOffsetY = self:scalePixelToScreenVector(HUDPopupMessage.POSITION.MESSAGE_TEXT) |
449 | self.inputRowsOffsetX, self.inputRowsOffsetY = self:scalePixelToScreenVector(HUDPopupMessage.POSITION.INPUT_ROWS) |
450 | self.skipButtonOffsetX, self.skipButtonOffsetY = self:scalePixelToScreenVector(HUDPopupMessage.POSITION.SKIP_BUTTON) |
451 | self.skipButtonWidth, self.skipButtonHeight = self:scalePixelToScreenVector(HUDPopupMessage.SIZE.SKIP_BUTTON) |
452 | |
453 | self.inputRowWidth, self.inputRowHeight = self:scalePixelToScreenVector(HUDPopupMessage.SIZE.INPUT_ROW) |
454 | self.borderPaddingX, self.borderPaddingY = self:scalePixelToScreenVector(HUDPopupMessage.SIZE.BORDER_PADDING) |
455 | |
456 | self.inputRowTextX, self.inputRowTextY = self:scalePixelToScreenVector(HUDPopupMessage.POSITION.INPUT_TEXT) |
457 | |
458 | self.titleTextSize = self:scalePixelToScreenHeight(HUDPopupMessage.TEXT_SIZE.TITLE) |
459 | self.textSize = self:scalePixelToScreenHeight(HUDPopupMessage.TEXT_SIZE.TEXT) |
460 | self.skipTextSize = self:scalePixelToScreenHeight(HUDPopupMessage.TEXT_SIZE.SKIP_TEXT) |
461 | end |
259 | function HUDPopupMessage:update(dt) |
260 | if not self.isMenuVisible then |
261 | HUDPopupMessage:superClass().update(self, dt) |
262 | |
263 | if not self.isGamePaused and not g_sleepManager:getIsSleeping() then |
264 | self.time = self.time + dt |
265 | self:updateCurrentMessage() |
266 | end |
267 | |
268 | if self:getVisible() then |
269 | local inputMode = self.inputManager:getInputHelpMode() |
270 | if inputMode ~= self.lastInputMode then |
271 | self.lastInputMode = inputMode |
272 | self:updateButtonGlyphs() |
273 | end |
274 | end |
275 | end |
276 | end |
301 | function HUDPopupMessage:updateButtonGlyphs() |
302 | if self.skipGlyph ~= nil then |
303 | self.skipGlyph:setAction(InputAction.SKIP_MESSAGE_BOX, self.l10n:getText(HUDPopupMessage.L10N_SYMBOL.BUTTON_OK), self.skipTextSize, true, false) |
304 | end |
305 | |
306 | if self.currentMessage ~= nil then |
307 | local controlIndex = 1 |
308 | for i = 1, HUDPopupMessage.MAX_INPUT_ROW_COUNT do |
309 | local rowIndex = HUDPopupMessage.MAX_INPUT_ROW_COUNT - i + 1 |
310 | local inputRowVisible = rowIndex <= #self.currentMessage.controls |
311 | self.inputRows[i]:setVisible(inputRowVisible) |
312 | |
313 | if inputRowVisible then |
314 | local control = self.currentMessage.controls[controlIndex] |
315 | self.inputGlyphs[i]:setActions(control:getActionNames(), "", self.textSize, false, false) |
316 | self.inputGlyphs[i]:setKeyboardGlyphColor(HUDPopupMessage.COLOR.INPUT_GLYPH) |
317 | controlIndex = controlIndex + 1 |
318 | end |
319 | end |
320 | end |
321 | end |
281 | function HUDPopupMessage:updateCurrentMessage() |
282 | if self.currentMessage ~= nil then |
283 | if self.time > self.currentMessage.duration then |
284 | self.time = -math.huge -- clear time to avoid double triggers |
285 | self:setVisible(false, true) -- animate out |
286 | end |
287 | elseif #self.pendingMessages > 0 then |
288 | self:startMessage() |
289 | self:setVisible(true, true) -- animate in |
290 | |
291 | self.animation:addCallback(function() |
292 | local x, y = self:getPosition() |
293 | g_depthOfFieldManager:pushArea(x, y, self:getWidth(), self:getHeight()) |
294 | self.blurAreaActive = true |
295 | end) |
296 | end |
297 | end |