diff --git a/sources/BaseAPI/API/Commands/BuiltInCommands/ACommandNotify.uc b/sources/BaseAPI/API/Commands/BuiltInCommands/ACommandNotify.uc
index c24e018..79fe238 100644
--- a/sources/BaseAPI/API/Commands/BuiltInCommands/ACommandNotify.uc
+++ b/sources/BaseAPI/API/Commands/BuiltInCommands/ACommandNotify.uc
@@ -19,7 +19,8 @@
* You should have received a copy of the GNU General Public License
* along with Acedia. If not, see .
*/
-class ACommandNotify extends Command;
+class ACommandNotify extends Command
+ dependsOn(ChatApi);
protected function BuildData(CommandDataBuilder builder) {
builder.Name(P("notify"));
diff --git a/sources/BaseAPI/API/Unflect/UnflectApi.uc b/sources/BaseAPI/API/Unflect/UnflectApi.uc
index a41e791..ed89d07 100644
--- a/sources/BaseAPI/API/Unflect/UnflectApi.uc
+++ b/sources/BaseAPI/API/Unflect/UnflectApi.uc
@@ -306,6 +306,7 @@ private final function bool _replaceFunction(Text oldFunctionLowerCase, Text new
_.memory.Free(initialCode);
}
replace.script = with.script;
+ Log("TRULLY!" @ replace @ with);
return true;
}
diff --git a/sources/Chat/ChatAPI.uc b/sources/Chat/ChatAPI.uc
index fefb49d..3864297 100644
--- a/sources/Chat/ChatAPI.uc
+++ b/sources/Chat/ChatAPI.uc
@@ -21,14 +21,19 @@
*/
class ChatApi extends AcediaObject;
-var private const int VOICE_MESSAGES_BEFORE_ACKNOWLEDGEMENTS;
-var private const int VOICE_MESSAGES_BEFORE_ALERTS;
-var private const int VOICE_MESSAGES_BEFORE_DIRECTIONS;
-var private const int VOICE_MESSAGES_BEFORE_INSULTS;
-var private const int VOICE_MESSAGES_BEFORE_TRADER;
-var private const int VOICE_MESSAGES_BEFORE_AUTO;
-var private const int VOICE_MESSAGES_TOTAL;
+///! API for accessing chat-related events.
+///!
+///! # Implementation
+///!
+///! Signal functions that track text chat messages `OnMessage()` and `OnMessageFor()` simply
+///! hook into [`BroadcastApi`] the first time such signal is requested.
+///!
+///! Signal function [`OnVoiceMessage()`] for tracking voice replaces a function in
+///! [`KFPlayerController`] to track when they are replicated.
+///! Then replaced function informs [`ChatApi`] about new voice message transmissions via
+///! internal [`_EmitOnVoiceMessage()`] method.
+/// Lists voice messages built-in in the game.
enum BuiltInVoiceMessage {
// Support
BIVM_SupportMedic,
@@ -98,41 +103,53 @@ enum BuiltInVoiceMessage {
BIVM_Unknown
};
+/// Killing Floor's native voice message is defined by `name` and `byte` pair.
+/// This struct is added to allow returning them as a pair.
struct NativeVoiceMessage {
var name type;
var byte index;
};
+/// Tracks whether we've already connected to broadcast signals.
var protected bool connectedToBroadcastAPI;
+/// Tracks whether we've already replaced a function that allows us to catch voice messages.
+var private bool replacedSendVoiceMessage;
+
+/// Auxiliary constants that store amount of values in [`BuiltInVoiceMessage`] before
+/// a certain group.
+/// Used for conversion between native voice messages and [`BuiltInVoiceMessage`]
+var private const int VOICE_MESSAGES_BEFORE_ACKNOWLEDGEMENTS;
+var private const int VOICE_MESSAGES_BEFORE_ALERTS;
+var private const int VOICE_MESSAGES_BEFORE_DIRECTIONS;
+var private const int VOICE_MESSAGES_BEFORE_INSULTS;
+var private const int VOICE_MESSAGES_BEFORE_TRADER;
+var private const int VOICE_MESSAGES_BEFORE_AUTO;
+var private const int VOICE_MESSAGES_TOTAL;
var protected ChatAPI_OnMessage_Signal onMessageSignal;
var protected ChatAPI_OnMessageFor_Signal onMessageForSignal;
+var protected ChatAPI_OnVoiceMessage_Signal onVoiceMessageSignal;
protected function Constructor() {
onMessageSignal = ChatAPI_OnMessage_Signal(_.memory.Allocate(class'ChatAPI_OnMessage_Signal'));
onMessageForSignal =
ChatAPI_OnMessageFor_Signal(_.memory.Allocate(class'ChatAPI_OnMessageFor_Signal'));
+ onVoiceMessageSignal =
+ ChatAPI_OnVoiceMessage_Signal(_.memory.Allocate(class'ChatAPI_OnVoiceMessage_Signal'));
}
protected function Finalizer() {
_.memory.Free(onMessageSignal);
_.memory.Free(onMessageForSignal);
+ _.memory.Free(onVoiceMessageSignal);
onMessageSignal = none;
onMessageForSignal = none;
+ onVoiceMessageSignal = none;
_server.unreal.broadcasts.OnHandleText(self).Disconnect();
_server.unreal.broadcasts.OnHandleTextFor(self).Disconnect();
connectedToBroadcastAPI = false;
}
-private final function TryConnectingBroadcastSignals() {
- if (connectedToBroadcastAPI) {
- return;
- }
- connectedToBroadcastAPI = true;
- _server.unreal.broadcasts.OnHandleText(self).connect = HandleText;
- _server.unreal.broadcasts.OnHandleTextFor(self).connect = HandleTextFor;
-}
-
/// Signal that will be emitted when a player sends a message into the chat.
///
/// Allows to modify message before sending it, as well as prevent it from being sent at all.
@@ -189,6 +206,27 @@ public /*signal*/ function ChatAPI_OnMessageFor_Slot OnMessageFor(AcediaObject r
return ChatAPI_OnMessageFor_Slot(onMessageForSignal.NewSlot(receiver));
}
+/// Signal that will be emitted when a player sends a voice message.
+///
+/// # Slot description
+///
+/// bool (EPlayer sender, ChatApi.BuiltInVoiceMessage message)
+///
+/// ## Parameters
+///
+/// * [`sender`]: `EPlayer` that has sent the voice message.
+/// * [`message`]: Message that `sender` has sent.
+public /*signal*/ function ChatAPI_OnVoiceMessage_Slot OnVoiceMessage(AcediaObject receiver) {
+ if (!replacedSendVoiceMessage) {
+ _.unflect.ReplaceFunction_S(
+ "KFMod.KFPlayerController.SendVoiceMessage",
+ "AcediaCore.Unflect_ChatApi_Controller.SendVoiceMessage",
+ "`ChatApi` was required to catch voice messages");
+ replacedSendVoiceMessage = true;
+ }
+ return ChatAPI_OnVoiceMessage_Slot(onVoiceMessageSignal.NewSlot(receiver));
+}
+
public /*internal*/ function NativeVoiceMessage _enumIntoNativeVoiceMessage(
BuiltInVoiceMessage voiceMessage
) {
@@ -275,6 +313,36 @@ public /*internal*/ function BuiltInVoiceMessage _nativeVoiceMessageIntoEnum(
return BIVM_Unknown;
}
+public final /*internal*/ /*native*/ function _EmitOnVoiceMessage(
+ PlayerController sender,
+ name messageType,
+ byte messageID
+) {
+ local EPlayer wrapper;
+ local NativeVoiceMessage nativeVoiceMessage;
+ local BuiltInVoiceMessage builtInVoiceMessage;
+
+ if (sender == none) return;
+ wrapper = _.players.FromController(sender);
+ if (wrapper == none) return;
+
+ nativeVoiceMessage.type = messageType;
+ nativeVoiceMessage.index = messageID;
+ builtInVoiceMessage = _nativeVoiceMessageIntoEnum(nativeVoiceMessage);
+ if (builtInVoiceMessage != BIVM_Unknown) {
+ onVoiceMessageSignal.Emit(wrapper, builtInVoiceMessage);
+ }
+ _.memory.Free(wrapper);
+}
+
+private final function TryConnectingBroadcastSignals() {
+ if (connectedToBroadcastAPI) {
+ return;
+ }
+ connectedToBroadcastAPI = true;
+ _server.unreal.broadcasts.OnHandleText(self).connect = HandleText;
+ _server.unreal.broadcasts.OnHandleTextFor(self).connect = HandleTextFor;
+}
private function bool HandleText(
Actor sender,
diff --git a/sources/Players/EPlayer.uc b/sources/Players/EPlayer.uc
index 9897f98..8eed122 100644
--- a/sources/Players/EPlayer.uc
+++ b/sources/Players/EPlayer.uc
@@ -507,15 +507,15 @@ public final function /* borrow */ ConsoleWriter BorrowConsole()
/// Sends specified voice message from the caller player to all players in the game.
public final function SendVoiceMessage(ChatApi.BuiltInVoiceMessage voiceMessage) {
- local PlayerController controller;
+ local PlayerController myController;
local ChatApi.NativeVoiceMessage nativeMessage;
if (voiceMessage == BIVM_Unknown) return;
- controller = GetController();
- if (controller == none) return;
+ myController = GetController();
+ if (myController == none) return;
nativeMessage = _.chat._enumIntoNativeVoiceMessage(voiceMessage);
- controller.ServerSpeech(nativeMessage.type, nativeMessage.index, "");
+ myController.ServerSpeech(nativeMessage.type, nativeMessage.index, "");
}
/// Sends a text message to notify the player about a particular event or situation, displayed