|
|
|
@ -22,15 +22,32 @@
|
|
|
|
|
class PlayerNotificationQueue extends AcediaObject |
|
|
|
|
config(AcediaSystem); |
|
|
|
|
|
|
|
|
|
/// Manages queue of notifications that should be displayed for a certain player. |
|
|
|
|
//! Manages queue of notifications that should be displayed for a certain player. |
|
|
|
|
//! |
|
|
|
|
//! Pushes messages one-by-one, once its their time to be displayed and making sure that only up to |
|
|
|
|
//! one message per channel is ever in queue. |
|
|
|
|
//! |
|
|
|
|
//! A channel is a way to group related notifications together. |
|
|
|
|
//! Its purpose is to control the display of notifications in a more organized and efficient manner. |
|
|
|
|
//! Each channel can only have one message in the queue at a time. |
|
|
|
|
//! This ensures that only the most relevant or up-to-date message is displayed to the player. |
|
|
|
|
|
|
|
|
|
/// Describes a single notification: title (optional, can be `none`) + message body and timeout |
|
|
|
|
/// Describes a single notification: title (optional, can be `none`) + message body, channel |
|
|
|
|
/// and timeout. |
|
|
|
|
struct Notification { |
|
|
|
|
var Text title; |
|
|
|
|
var Text body; |
|
|
|
|
var float duration; |
|
|
|
|
var Text channel; |
|
|
|
|
}; |
|
|
|
|
var private array<Notification> notificationQueue; |
|
|
|
|
|
|
|
|
|
/// We need this variable to keep track of which channel the currently displayed message belongs to, |
|
|
|
|
/// so that we can ensure that only one message is displayed at a time for each channel. |
|
|
|
|
/// This variable allows us to easily check if a new message belongs to the same channel as |
|
|
|
|
/// the currently displayed message, and if so, replace the currently displayed message with |
|
|
|
|
/// the new one. |
|
|
|
|
var private Text currentChannel; |
|
|
|
|
/// Reference to the `PlayerController` for the player that owns this queue |
|
|
|
|
var private NativeActorRef playerControllerRef; |
|
|
|
|
/// Timer until next notification can be displayed |
|
|
|
@ -80,8 +97,29 @@ public final /*native*/ function SetupController(NativeActorRef newPlayerControl
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Add new notification to the queue |
|
|
|
|
public final function AddNotification(BaseText title, BaseText body, float duration) { |
|
|
|
|
/// Sends a text message to notify the player about a particular event or situation, displayed |
|
|
|
|
/// as a notification. |
|
|
|
|
/// |
|
|
|
|
/// While the header parameter is optional, it is recommended to provide a meaningful header |
|
|
|
|
/// to give the player context about the notification. The duration parameter suggests the time |
|
|
|
|
/// that the message should be displayed for, but the actual duration may be limited by the |
|
|
|
|
/// implementation or server settings. |
|
|
|
|
/// |
|
|
|
|
/// The [`channel`] parameter (case-sensitive) allows you to group related notifications together by |
|
|
|
|
/// assigning them to a specific channel. |
|
|
|
|
/// Each channel can only have one message in the queue at any given time. |
|
|
|
|
/// For instance, when conducting a vote, the old message about the vote's start can be quickly |
|
|
|
|
/// replaced with a newer message about the vote's end. |
|
|
|
|
/// Notifications without a channel `none` can have any number of messages queued up. |
|
|
|
|
/// |
|
|
|
|
/// Acedia uses "voting" channel for voting. |
|
|
|
|
public final function AddNotification( |
|
|
|
|
BaseText title, |
|
|
|
|
BaseText body, |
|
|
|
|
float duration, |
|
|
|
|
BaseText channel |
|
|
|
|
) { |
|
|
|
|
local int i, newNotificationIndex; |
|
|
|
|
local Notification newNotification; |
|
|
|
|
|
|
|
|
|
if (body == none) { |
|
|
|
@ -90,9 +128,30 @@ public final function AddNotification(BaseText title, BaseText body, float durat
|
|
|
|
|
if (title != none) { |
|
|
|
|
newNotification.title = title.Copy(); |
|
|
|
|
} |
|
|
|
|
if (channel != none) { |
|
|
|
|
newNotification.channel = channel.Copy(); |
|
|
|
|
} |
|
|
|
|
newNotification.body = body.Copy(); |
|
|
|
|
newNotification.duration = duration; |
|
|
|
|
notificationQueue[notificationQueue.length] = newNotification; |
|
|
|
|
newNotificationIndex = notificationQueue.length; |
|
|
|
|
// Make sure there is only one message per channel by replacing the old one |
|
|
|
|
if (currentChannel != none && currentChannel.Compare(channel)) { |
|
|
|
|
SetupNotification(/*take*/ newNotification); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
if (channel != none) { |
|
|
|
|
for (i = 0; i < notificationQueue.length; i += 1) { |
|
|
|
|
if (channel.Compare(notificationQueue[i].channel)) { |
|
|
|
|
_.memory.Free3( |
|
|
|
|
notificationQueue[i].title, |
|
|
|
|
notificationQueue[i].body, |
|
|
|
|
notificationQueue[i].channel); |
|
|
|
|
newNotificationIndex = i; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
notificationQueue[newNotificationIndex] = newNotification; |
|
|
|
|
if (!IsUpdateScheduled()) { |
|
|
|
|
SetupNextNotification(none); |
|
|
|
|
} |
|
|
|
@ -177,9 +236,20 @@ private function PrintNotifcationAt(
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private function SetupNextNotification(Timer callerInstance) { |
|
|
|
|
local Notification nextNotification; |
|
|
|
|
|
|
|
|
|
if (notificationQueue.length <= 0) { |
|
|
|
|
InterruptScheduling(); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
nextNotification = notificationQueue[0]; |
|
|
|
|
notificationQueue.Remove(0, 1); |
|
|
|
|
SetupNotification(/*take*/ nextNotification); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private function SetupNotification(/*take*/ Notification notification) { |
|
|
|
|
local int titleShift; |
|
|
|
|
local MutableText upperCaseTitle; |
|
|
|
|
local Notification nextNotification; |
|
|
|
|
local PlayerController playerController; |
|
|
|
|
|
|
|
|
|
// Get appropriate [`PlayerController`] and next notification |
|
|
|
@ -187,31 +257,30 @@ private function SetupNextNotification(Timer callerInstance) {
|
|
|
|
|
if (playerController == none) { |
|
|
|
|
_.memory.Free(playerControllerRef); |
|
|
|
|
playerControllerRef = none; |
|
|
|
|
} |
|
|
|
|
if (notificationQueue.length <= 0 || playerController == none) { |
|
|
|
|
InterruptScheduling(); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
nextNotification = notificationQueue[0]; |
|
|
|
|
notificationQueue.Remove(0, 1); |
|
|
|
|
nextNotification.duration = FMin(nextNotification.duration, maximumNotifyTime); |
|
|
|
|
if (nextNotification.duration <= 0) { |
|
|
|
|
nextNotification.duration = 10.0; |
|
|
|
|
_.memory.Free(currentChannel); |
|
|
|
|
currentChannel = notification.channel; |
|
|
|
|
notification.duration = FMin(notification.duration, maximumNotifyTime); |
|
|
|
|
if (notification.duration <= 0) { |
|
|
|
|
notification.duration = 10.0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// And print |
|
|
|
|
playerController.ClearProgressMessages(); |
|
|
|
|
playerController.SetProgressTime(nextNotification.duration); |
|
|
|
|
if (nextNotification.title != none) { |
|
|
|
|
upperCaseTitle = nextNotification.title.UpperMutableCopy(); |
|
|
|
|
playerController.SetProgressTime(notification.duration); |
|
|
|
|
if (notification.title != none) { |
|
|
|
|
upperCaseTitle = notification.title.UpperMutableCopy(); |
|
|
|
|
upperCaseTitle.ChangeDefaultColor(_.color.TextHeader); |
|
|
|
|
PrintNotifcationAt(playerController, upperCaseTitle, 0, 1); |
|
|
|
|
titleShift = 1; |
|
|
|
|
_.memory.Free(upperCaseTitle); |
|
|
|
|
} |
|
|
|
|
PrintNotifcationAt(playerController, nextNotification.body, titleShift, 4 - titleShift); |
|
|
|
|
ScheduleUpdate(nextNotification.duration); |
|
|
|
|
_.memory.Free2(nextNotification.title, nextNotification.body); |
|
|
|
|
PrintNotifcationAt(playerController, notification.body, titleShift, 4 - titleShift); |
|
|
|
|
ScheduleUpdate(notification.duration); |
|
|
|
|
// We moved channel's reference into `currentChannel` |
|
|
|
|
_.memory.Free2(notification.title, notification.body); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
defaultproperties { |
|
|
|
|