|
|
|
/**
|
|
|
|
* Author: dkanus
|
|
|
|
* Home repo: https://www.insultplayers.ru/git/AcediaFramework/AcediaCore
|
|
|
|
* License: GPL
|
|
|
|
* Copyright 2022-2023 Anton Tarasenko
|
|
|
|
*------------------------------------------------------------------------------
|
|
|
|
* This file is part of Acedia.
|
|
|
|
*
|
|
|
|
* Acedia is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* Acedia is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
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;
|
|
|
|
|
|
|
|
enum BuiltInVoiceMessage {
|
|
|
|
// Support
|
|
|
|
BIVM_SupportMedic,
|
|
|
|
BIVM_SupportHelp,
|
|
|
|
BIVM_SupportAskForMoney,
|
|
|
|
BIVM_SupportAskForWeapon,
|
|
|
|
// Acknowledgements
|
|
|
|
BIVM_AckYes,
|
|
|
|
BIVM_AckNo,
|
|
|
|
BIVM_AckThanks,
|
|
|
|
BIVM_AckSorry,
|
|
|
|
// Alert
|
|
|
|
BIVM_AlretLookOut,
|
|
|
|
BIVM_AlretRun,
|
|
|
|
BIVM_AlretWaitForMe,
|
|
|
|
BIVM_AlretWeldTheDoors,
|
|
|
|
BIVM_AlretLetsHoleUpHere,
|
|
|
|
BIVM_AlretFollowMe,
|
|
|
|
// Direction
|
|
|
|
BIVM_DirectionGetToTheTrader,
|
|
|
|
BIVM_DirectionGoUpstairs,
|
|
|
|
BIVM_DirectionGoDownstairs,
|
|
|
|
BIVM_DirectionGetOutside,
|
|
|
|
BIVM_DirectionGetInside,
|
|
|
|
// Insult
|
|
|
|
BIVM_InsultSpecimens,
|
|
|
|
BIVM_InsultPlayers,
|
|
|
|
// Trader
|
|
|
|
BIVM_TraderCheckWhereTheShopIs,
|
|
|
|
BIVM_TraderGetClose,
|
|
|
|
BIVM_TraderShopOpen,
|
|
|
|
BIVM_TraderShopOpenFinal,
|
|
|
|
BIVM_Trader30SecondsUntilShopCloses,
|
|
|
|
BIVM_TraderShopClosed,
|
|
|
|
BIVM_TraderCompliment,
|
|
|
|
BIVM_TraderNotEnoughMoney,
|
|
|
|
BIVM_TraderCannotCarry,
|
|
|
|
BIVM_TraderHurryUp1,
|
|
|
|
BIVM_TraderHurryUp2,
|
|
|
|
// Auto
|
|
|
|
BIVM_AutoWelding,
|
|
|
|
BIVM_AutoUnwelding,
|
|
|
|
BIVM_AutoReload,
|
|
|
|
BIVM_AutoOutOfAmmo,
|
|
|
|
BIVM_AutoDosh,
|
|
|
|
BIVM_AutoStandStillTryingToHealYou,
|
|
|
|
BIVM_AutoLowOnHealth,
|
|
|
|
BIVM_AutoBloatAcid,
|
|
|
|
BIVM_AutoPatriarchCloack,
|
|
|
|
BIVM_AutoPatriarchMinigun,
|
|
|
|
BIVM_AutoPatriarchRocketLauncher,
|
|
|
|
BIVM_AutoGrabbedByClot,
|
|
|
|
BIVM_AutoFleshpoundSpotted,
|
|
|
|
BIVM_AutoGorefastSpotted,
|
|
|
|
BIVM_AutoScrakeSpotted,
|
|
|
|
BIVM_AutoSirenSpotten,
|
|
|
|
BIVM_AutoSirenScream,
|
|
|
|
BIVM_AutoStalkerSpotted,
|
|
|
|
BIVM_AutoCrawlertSpotted,
|
|
|
|
BIVM_AutoMeleeKilledAStalker,
|
|
|
|
BIVM_AutoUsingFlamethrower,
|
|
|
|
BIVM_AutoEquipHuntingShotgun,
|
|
|
|
BIVM_AutoEquipHandcannons,
|
|
|
|
BIVM_AutoEquipLAW,
|
|
|
|
BIVM_AutoEquipFireaxe,
|
|
|
|
// Fallback
|
|
|
|
BIVM_Unknown
|
|
|
|
};
|
|
|
|
|
|
|
|
struct NativeVoiceMessage {
|
|
|
|
var name type;
|
|
|
|
var byte index;
|
|
|
|
};
|
|
|
|
|
|
|
|
var protected bool connectedToBroadcastAPI;
|
|
|
|
|
|
|
|
var protected ChatAPI_OnMessage_Signal onMessageSignal;
|
|
|
|
var protected ChatAPI_OnMessageFor_Signal onMessageForSignal;
|
|
|
|
|
|
|
|
protected function Constructor() {
|
|
|
|
onMessageSignal = ChatAPI_OnMessage_Signal(_.memory.Allocate(class'ChatAPI_OnMessage_Signal'));
|
|
|
|
onMessageForSignal =
|
|
|
|
ChatAPI_OnMessageFor_Signal(_.memory.Allocate(class'ChatAPI_OnMessageFor_Signal'));
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function Finalizer() {
|
|
|
|
_.memory.Free(onMessageSignal);
|
|
|
|
_.memory.Free(onMessageForSignal);
|
|
|
|
onMessageSignal = none;
|
|
|
|
onMessageForSignal = 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.
|
|
|
|
///
|
|
|
|
/// Return `false` to prevent message from being sent.
|
|
|
|
/// If `false` is returned, signal propagation to the remaining handlers will also be interrupted.
|
|
|
|
///
|
|
|
|
/// # Slot description
|
|
|
|
///
|
|
|
|
/// bool <slot>(EPlayer sender, MutableText message, bool teamMessage)
|
|
|
|
///
|
|
|
|
/// ## Parameters
|
|
|
|
///
|
|
|
|
/// * [`sender`]: `EPlayer` that has sent the message.
|
|
|
|
/// * [`message`]: Message that `sender` has sent.
|
|
|
|
/// This is a mutable variable and can be modified from message will be sent.
|
|
|
|
/// * [`teamMessage`]: Is this a team message (to be sent only to players on the same team)?
|
|
|
|
///
|
|
|
|
/// ## Returns
|
|
|
|
///
|
|
|
|
/// Return `false` to prevent this message from being sent at all and `true` otherwise.
|
|
|
|
/// Message will be sent only if all handlers will return `true`.
|
|
|
|
public /*signal*/ function ChatAPI_OnMessage_Slot OnMessage(AcediaObject receiver) {
|
|
|
|
TryConnectingBroadcastSignals();
|
|
|
|
return ChatAPI_OnMessage_Slot(onMessageSignal.NewSlot(receiver));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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.
|
|
|
|
///
|
|
|
|
/// Return `false` to prevent message from being sent to a specific player.
|
|
|
|
/// If `false` is returned, signal propagation to the remaining handlers will also be interrupted.
|
|
|
|
///
|
|
|
|
/// # Slot description
|
|
|
|
///
|
|
|
|
/// bool <slot>(EPlayer receiver, EPlayer sender, BaseText message)
|
|
|
|
///
|
|
|
|
/// ## Parameters
|
|
|
|
///
|
|
|
|
/// * [`receiver`]: `EPlayer` that will receive the message.
|
|
|
|
/// * [`sender`]: `EPlayer` that has sent the message.
|
|
|
|
/// * [`message`]: Message that `sender` has sent. This is an immutable variable and cannot
|
|
|
|
/// be changed at this point. Use `OnMessage()` signal function for that.
|
|
|
|
///
|
|
|
|
/// ## Returns
|
|
|
|
///
|
|
|
|
/// Return `false` to prevent this message from being sent to a particular player and
|
|
|
|
/// `true` otherwise.
|
|
|
|
/// Message will be sent only if all handlers will return `true`.
|
|
|
|
/// However decision whether to send message or not is made for every player separately.
|
|
|
|
public /*signal*/ function ChatAPI_OnMessageFor_Slot OnMessageFor(AcediaObject receiver) {
|
|
|
|
TryConnectingBroadcastSignals();
|
|
|
|
return ChatAPI_OnMessageFor_Slot(onMessageForSignal.NewSlot(receiver));
|
|
|
|
}
|
|
|
|
|
|
|
|
public /*internal*/ function NativeVoiceMessage _enumIntoNativeVoiceMessage(
|
|
|
|
BuiltInVoiceMessage voiceMessage
|
|
|
|
) {
|
|
|
|
local int enumValue;
|
|
|
|
local NativeVoiceMessage result;
|
|
|
|
|
|
|
|
enumValue = int(voiceMessage);
|
|
|
|
if (enumValue < VOICE_MESSAGES_BEFORE_ACKNOWLEDGEMENTS) {
|
|
|
|
result.type = 'SUPPORT';
|
|
|
|
result.index = enumValue;
|
|
|
|
} else if (enumValue < VOICE_MESSAGES_BEFORE_ALERTS) {
|
|
|
|
result.type = 'ACK';
|
|
|
|
result.index = enumValue - VOICE_MESSAGES_BEFORE_ACKNOWLEDGEMENTS;
|
|
|
|
} else if (enumValue < VOICE_MESSAGES_BEFORE_DIRECTIONS) {
|
|
|
|
result.type = 'ALERT';
|
|
|
|
result.index = enumValue - VOICE_MESSAGES_BEFORE_ALERTS;
|
|
|
|
} else if (enumValue < VOICE_MESSAGES_BEFORE_INSULTS) {
|
|
|
|
result.type = 'DIRECTION';
|
|
|
|
result.index = enumValue - VOICE_MESSAGES_BEFORE_DIRECTIONS;
|
|
|
|
} else if (enumValue < VOICE_MESSAGES_BEFORE_TRADER) {
|
|
|
|
result.type = 'INSULT';
|
|
|
|
result.index = enumValue - VOICE_MESSAGES_BEFORE_INSULTS;
|
|
|
|
} else if (enumValue < VOICE_MESSAGES_BEFORE_AUTO) {
|
|
|
|
result.type = 'TRADER';
|
|
|
|
result.index = enumValue - VOICE_MESSAGES_BEFORE_TRADER;
|
|
|
|
if (result.index >= 5) {
|
|
|
|
result.index += 1;
|
|
|
|
}
|
|
|
|
} else if (enumValue < VOICE_MESSAGES_TOTAL) {
|
|
|
|
result.type = 'AUTO';
|
|
|
|
result.index = enumValue - VOICE_MESSAGES_BEFORE_AUTO;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
public /*internal*/ function BuiltInVoiceMessage _nativeVoiceMessageIntoEnum(
|
|
|
|
NativeVoiceMessage voiceMessage
|
|
|
|
) {
|
|
|
|
switch (voiceMessage.type) {
|
|
|
|
case 'SUPPORT':
|
|
|
|
if (voiceMessage.index < 4) {
|
|
|
|
return BuiltInVoiceMessage(voiceMessage.index);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'ACK':
|
|
|
|
if (voiceMessage.index < 4) {
|
|
|
|
return BuiltInVoiceMessage(
|
|
|
|
VOICE_MESSAGES_BEFORE_ACKNOWLEDGEMENTS + voiceMessage.index);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'ALERT':
|
|
|
|
if (voiceMessage.index < 6) {
|
|
|
|
return BuiltInVoiceMessage(VOICE_MESSAGES_BEFORE_ALERTS + voiceMessage.index);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'DIRECTION':
|
|
|
|
if (voiceMessage.index < 5) {
|
|
|
|
return BuiltInVoiceMessage(VOICE_MESSAGES_BEFORE_DIRECTIONS + voiceMessage.index);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'INSULT':
|
|
|
|
if (voiceMessage.index < 2) {
|
|
|
|
return BuiltInVoiceMessage(VOICE_MESSAGES_BEFORE_INSULTS + voiceMessage.index);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'TRADER':
|
|
|
|
if (voiceMessage.index < 12) {
|
|
|
|
if (voiceMessage.index < 5) {
|
|
|
|
return BuiltInVoiceMessage(VOICE_MESSAGES_BEFORE_TRADER + voiceMessage.index);
|
|
|
|
} else if (voiceMessage.index > 5) {
|
|
|
|
return BuiltInVoiceMessage(
|
|
|
|
VOICE_MESSAGES_BEFORE_TRADER + voiceMessage.index - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'AUTO':
|
|
|
|
if (voiceMessage.index < 25) {
|
|
|
|
return BuiltInVoiceMessage(VOICE_MESSAGES_BEFORE_AUTO + voiceMessage.index);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return BIVM_Unknown;
|
|
|
|
}
|
|
|
|
return BIVM_Unknown;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private function bool HandleText(
|
|
|
|
Actor sender,
|
|
|
|
out string message,
|
|
|
|
name messageType,
|
|
|
|
bool teamMessage
|
|
|
|
) {
|
|
|
|
local bool result;
|
|
|
|
local MutableText messageAsText;
|
|
|
|
local EPlayer senderPlayer;
|
|
|
|
|
|
|
|
// We only want to catch chat messages from a player
|
|
|
|
if (messageType != 'Say' && messageType != 'TeamSay') return true;
|
|
|
|
senderPlayer = _.players.FromController(PlayerController(sender));
|
|
|
|
if (senderPlayer == none) return true;
|
|
|
|
|
|
|
|
messageAsText = __().text.FromColoredStringM(message);
|
|
|
|
result = onMessageSignal.Emit(senderPlayer, messageAsText, teamMessage);
|
|
|
|
message = messageAsText.ToColoredString();
|
|
|
|
// To correctly display chat messages we want to drop default color tag
|
|
|
|
// at the beginning (the one `ToColoredString()` adds if first character
|
|
|
|
// has no defined color).
|
|
|
|
// This is a compatibility consideration with vanilla UI that expects
|
|
|
|
// uncolored text. Not removing initial color tag will make chat text
|
|
|
|
// appear black.
|
|
|
|
if (!messageAsText.GetFormatting(0).isColored) {
|
|
|
|
message = Mid(message, 4);
|
|
|
|
}
|
|
|
|
_.memory.Free(messageAsText);
|
|
|
|
_.memory.Free(senderPlayer);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function bool HandleTextFor(
|
|
|
|
PlayerController receiver,
|
|
|
|
Actor sender,
|
|
|
|
out string message,
|
|
|
|
name messageType
|
|
|
|
) {
|
|
|
|
local bool result;
|
|
|
|
local Text messageAsText;
|
|
|
|
local EPlayer senderPlayer, receiverPlayer;
|
|
|
|
|
|
|
|
// We only want to catch chat messages from another player
|
|
|
|
if (messageType != 'Say' && messageType != 'TeamSay') return true;
|
|
|
|
senderPlayer = _.players.FromController(PlayerController(sender));
|
|
|
|
if (senderPlayer == none) return true;
|
|
|
|
|
|
|
|
receiverPlayer = _.players.FromController(receiver);
|
|
|
|
if (receiverPlayer == none) {
|
|
|
|
_.memory.Free(senderPlayer);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
messageAsText = __().text.FromColoredString(message);
|
|
|
|
result = onMessageForSignal.Emit(receiverPlayer, senderPlayer, messageAsText);
|
|
|
|
_.memory.Free(messageAsText);
|
|
|
|
_.memory.Free(senderPlayer);
|
|
|
|
_.memory.Free(receiverPlayer);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
defaultproperties {
|
|
|
|
VOICE_MESSAGES_BEFORE_ACKNOWLEDGEMENTS = 4
|
|
|
|
VOICE_MESSAGES_BEFORE_ALERTS = 8
|
|
|
|
VOICE_MESSAGES_BEFORE_DIRECTIONS = 14
|
|
|
|
VOICE_MESSAGES_BEFORE_INSULTS = 19
|
|
|
|
VOICE_MESSAGES_BEFORE_TRADER = 21
|
|
|
|
VOICE_MESSAGES_BEFORE_AUTO = 32
|
|
|
|
VOICE_MESSAGES_TOTAL = 57
|
|
|
|
}
|