Typing indicator (typing chat bubble) (#8215)
86
Content.Client/Chat/TypingIndicator/TypingIndicatorSystem.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Chat.TypingIndicator;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Chat.TypingIndicator;
|
||||
|
||||
// Client-side typing system tracks user input in chat box
|
||||
public sealed class TypingIndicatorSystem : SharedTypingIndicatorSystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _time = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
private readonly TimeSpan _typingTimeout = TimeSpan.FromSeconds(2);
|
||||
private TimeSpan _lastTextChange;
|
||||
private bool _isClientTyping;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_cfg.OnValueChanged(CCVars.ChatShowTypingIndicator, OnShowTypingChanged);
|
||||
}
|
||||
|
||||
public void ClientChangedChatText()
|
||||
{
|
||||
// don't update it if player don't want to show typing indicator
|
||||
if (!_cfg.GetCVar(CCVars.ChatShowTypingIndicator))
|
||||
return;
|
||||
|
||||
// client typed something - show typing indicator
|
||||
ClientUpdateTyping(true);
|
||||
_lastTextChange = _time.CurTime;
|
||||
}
|
||||
|
||||
public void ClientSubmittedChatText()
|
||||
{
|
||||
// don't update it if player don't want to show typing
|
||||
if (!_cfg.GetCVar(CCVars.ChatShowTypingIndicator))
|
||||
return;
|
||||
|
||||
// client submitted text - hide typing indicator
|
||||
ClientUpdateTyping(false);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
// check if client didn't changed chat text box for a long time
|
||||
if (_isClientTyping)
|
||||
{
|
||||
var dif = _time.CurTime - _lastTextChange;
|
||||
if (dif > _typingTimeout)
|
||||
{
|
||||
// client didn't typed anything for a long time - hide indicator
|
||||
ClientUpdateTyping(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ClientUpdateTyping(bool isClientTyping)
|
||||
{
|
||||
if (_isClientTyping == isClientTyping)
|
||||
return;
|
||||
_isClientTyping = isClientTyping;
|
||||
|
||||
// check if player controls any pawn
|
||||
var playerPawn = _playerManager.LocalPlayer?.ControlledEntity;
|
||||
if (playerPawn == null)
|
||||
return;
|
||||
|
||||
// send a networked event to server
|
||||
RaiseNetworkEvent(new TypingChangedEvent(playerPawn.Value, isClientTyping));
|
||||
}
|
||||
|
||||
private void OnShowTypingChanged(bool showTyping)
|
||||
{
|
||||
// hide typing indicator immediately if player don't want to show it anymore
|
||||
if (!showTyping)
|
||||
{
|
||||
ClientUpdateTyping(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using Content.Shared.Chat.TypingIndicator;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Chat.TypingIndicator;
|
||||
|
||||
public sealed class TypingIndicatorVisualizerSystem : VisualizerSystem<TypingIndicatorComponent>
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<TypingIndicatorComponent, ComponentInit>(OnInit);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, TypingIndicatorComponent component, ComponentInit args)
|
||||
{
|
||||
if (!TryComp(uid, out SpriteComponent? sprite))
|
||||
return;
|
||||
|
||||
if (!_prototypeManager.TryIndex<TypingIndicatorPrototype>(component.Prototype, out var proto))
|
||||
{
|
||||
Logger.Error($"Unknown typing indicator id: {component.Prototype}");
|
||||
return;
|
||||
}
|
||||
|
||||
var layer = sprite.LayerMapReserveBlank(TypingIndicatorLayers.Base);
|
||||
sprite.LayerSetRSI(layer, proto.SpritePath);
|
||||
sprite.LayerSetState(layer, proto.TypingState);
|
||||
sprite.LayerSetShader(layer, proto.Shader);
|
||||
sprite.LayerSetOffset(layer, proto.Offset);
|
||||
sprite.LayerSetVisible(layer, false);
|
||||
}
|
||||
|
||||
protected override void OnAppearanceChange(EntityUid uid, TypingIndicatorComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
base.OnAppearanceChange(uid, component, ref args);
|
||||
|
||||
if (!TryComp(uid, out SpriteComponent? sprite))
|
||||
return;
|
||||
|
||||
args.Component.TryGetData(TypingIndicatorVisuals.IsTyping, out bool isTyping);
|
||||
if (sprite.LayerMapTryGet(TypingIndicatorLayers.Base, out var layer))
|
||||
{
|
||||
sprite.LayerSetVisible(layer, isTyping);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Client.Alerts.UI;
|
||||
using Content.Client.Chat.Managers;
|
||||
using Content.Client.Chat.TypingIndicator;
|
||||
using Content.Client.Resources;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Chat;
|
||||
@@ -475,6 +476,9 @@ namespace Content.Client.Chat.UI
|
||||
{
|
||||
// Update channel select button to correct channel if we have a prefix.
|
||||
UpdateChannelSelectButton();
|
||||
|
||||
// Warn typing indicator about change
|
||||
EntitySystem.Get<TypingIndicatorSystem>().ClientChangedChatText();
|
||||
}
|
||||
|
||||
private static ChatSelectChannel GetChannelFromPrefix(char prefix)
|
||||
@@ -518,6 +522,9 @@ namespace Content.Client.Chat.UI
|
||||
|
||||
private void Input_OnTextEntered(LineEdit.LineEditEventArgs args)
|
||||
{
|
||||
// Warn typing indicator about entered text
|
||||
EntitySystem.Get<TypingIndicatorSystem>().ClientSubmittedChatText();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(args.Text))
|
||||
{
|
||||
var (prefixChannel, text) = SplitInputContents();
|
||||
|
||||
63
Content.Server/Chat/TypingIndicator/TypingIndicatorSystem.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Chat.TypingIndicator;
|
||||
using Robust.Server.GameObjects;
|
||||
|
||||
namespace Content.Server.Chat.TypingIndicator;
|
||||
|
||||
// Server-side typing system
|
||||
// It receives networked typing events from clients
|
||||
// And sync typing indicator using appearance component
|
||||
public sealed class TypingIndicatorSystem : SharedTypingIndicatorSystem
|
||||
{
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<PlayerAttachedEvent>(OnPlayerAttached);
|
||||
SubscribeLocalEvent<TypingIndicatorComponent, PlayerDetachedEvent>(OnPlayerDetached);
|
||||
SubscribeNetworkEvent<TypingChangedEvent>(OnClientTypingChanged);
|
||||
}
|
||||
|
||||
private void OnPlayerAttached(PlayerAttachedEvent ev)
|
||||
{
|
||||
// when player poses entity we want to make sure that there is typing indicator
|
||||
EnsureComp<TypingIndicatorComponent>(ev.Entity);
|
||||
// we also need appearance component to sync visual state
|
||||
EnsureComp<ServerAppearanceComponent>(ev.Entity);
|
||||
}
|
||||
|
||||
private void OnPlayerDetached(EntityUid uid, TypingIndicatorComponent component, PlayerDetachedEvent args)
|
||||
{
|
||||
// player left entity body - hide typing indicator
|
||||
SetTypingIndicatorEnabled(uid, false);
|
||||
}
|
||||
|
||||
private void OnClientTypingChanged(TypingChangedEvent ev)
|
||||
{
|
||||
// make sure that this entity still exist
|
||||
if (!Exists(ev.Uid))
|
||||
{
|
||||
Logger.Warning($"Client attached entity {ev.Uid} from TypingChangedEvent doesn't exist on server.");
|
||||
return;
|
||||
}
|
||||
|
||||
// check if this entity can speak or emote
|
||||
if (!_actionBlocker.CanSpeak(ev.Uid) && !_actionBlocker.CanEmote(ev.Uid))
|
||||
{
|
||||
// nah, make sure that typing indicator is disabled
|
||||
SetTypingIndicatorEnabled(ev.Uid, false);
|
||||
return;
|
||||
}
|
||||
|
||||
SetTypingIndicatorEnabled(ev.Uid, ev.IsTyping);
|
||||
}
|
||||
|
||||
private void SetTypingIndicatorEnabled(EntityUid uid, bool isEnabled, AppearanceComponent? appearance = null)
|
||||
{
|
||||
if (!Resolve(uid, ref appearance, false))
|
||||
return;
|
||||
|
||||
appearance.SetData(TypingIndicatorVisuals.IsTyping, isEnabled);
|
||||
}
|
||||
}
|
||||
@@ -820,6 +820,9 @@ namespace Content.Shared.CCVar
|
||||
public static readonly CVarDef<bool> ChatSanitizerEnabled =
|
||||
CVarDef.Create("chat.chat_sanitizer_enabled", true, CVar.SERVERONLY);
|
||||
|
||||
public static readonly CVarDef<bool> ChatShowTypingIndicator =
|
||||
CVarDef.Create("chat.show_typing_indicator", true, CVar.CLIENTONLY);
|
||||
|
||||
/*
|
||||
* AFK
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Content.Shared.Chat.TypingIndicator;
|
||||
|
||||
/// <summary>
|
||||
/// Sync typing indicator icon between client and server.
|
||||
/// </summary>
|
||||
public abstract class SharedTypingIndicatorSystem : EntitySystem
|
||||
{
|
||||
|
||||
}
|
||||
20
Content.Shared/Chat/TypingIndicator/TypingChangedEvent.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Chat.TypingIndicator;
|
||||
|
||||
/// <summary>
|
||||
/// Networked event from client.
|
||||
/// Send to server when client started/stopped typing in chat input field.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class TypingChangedEvent : EntityEventArgs
|
||||
{
|
||||
public readonly EntityUid Uid;
|
||||
public readonly bool IsTyping;
|
||||
|
||||
public TypingChangedEvent(EntityUid uid, bool isTyping)
|
||||
{
|
||||
Uid = uid;
|
||||
IsTyping = isTyping;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Shared.Chat.TypingIndicator;
|
||||
|
||||
/// <summary>
|
||||
/// Show typing indicator icon when player typing text in chat box.
|
||||
/// Added automatically when player poses entity.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
[Friend(typeof(SharedTypingIndicatorSystem))]
|
||||
public sealed class TypingIndicatorComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Prototype id that store all visual info about typing indicator.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
[DataField("proto", customTypeSerializer: typeof(PrototypeIdSerializer<TypingIndicatorPrototype>))]
|
||||
public string Prototype = "default";
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Chat.TypingIndicator;
|
||||
|
||||
/// <summary>
|
||||
/// Prototype to store chat typing indicator visuals.
|
||||
/// </summary>
|
||||
[Prototype("typingIndicator")]
|
||||
public sealed class TypingIndicatorPrototype : IPrototype
|
||||
{
|
||||
[IdDataFieldAttribute]
|
||||
public string ID { get; } = default!;
|
||||
|
||||
[DataField("spritePath")]
|
||||
public ResourcePath SpritePath = new("/Textures/Effects/speech.rsi");
|
||||
|
||||
[DataField("typingState", required: true)]
|
||||
public string TypingState = default!;
|
||||
|
||||
[DataField("offset")]
|
||||
public Vector2 Offset = new(0.5f, 0.5f);
|
||||
|
||||
[DataField("shader")]
|
||||
public string Shader = "unshaded";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Chat.TypingIndicator;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum TypingIndicatorVisuals : byte
|
||||
{
|
||||
IsTyping
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public enum TypingIndicatorLayers : byte
|
||||
{
|
||||
Base
|
||||
}
|
||||
@@ -68,6 +68,8 @@
|
||||
Otherwise, wreak havoc on the station!
|
||||
- type: ReplacementAccent
|
||||
accent: genericAggressive
|
||||
- type: TypingIndicator
|
||||
proto: alien
|
||||
- type: NoSlip
|
||||
|
||||
- type: entity
|
||||
@@ -84,6 +86,8 @@
|
||||
sprite: Mobs/Aliens/Carps/magic.rsi
|
||||
- type: GhostTakeoverAvailable
|
||||
name: magicarp
|
||||
- type: TypingIndicator
|
||||
proto: guardian
|
||||
|
||||
- type: entity
|
||||
name: holocarp
|
||||
@@ -110,6 +114,8 @@
|
||||
- Opaque
|
||||
- type: GhostTakeoverAvailable
|
||||
name: holocarp
|
||||
- type: TypingIndicator
|
||||
proto: robot
|
||||
|
||||
- type: entity
|
||||
id: MobCarpSalvage
|
||||
|
||||
@@ -79,6 +79,8 @@
|
||||
rules: You are an antagonist, smack, slash, and wack!
|
||||
- type: ReplacementAccent
|
||||
accent: xeno
|
||||
- type: TypingIndicator
|
||||
proto: alien
|
||||
- type: Temperature
|
||||
heatDamageThreshold: 360
|
||||
coldDamageThreshold: -150
|
||||
|
||||
@@ -56,6 +56,8 @@
|
||||
- type: Internals
|
||||
- type: Examiner
|
||||
- type: Speech
|
||||
- type: TypingIndicator
|
||||
proto: guardian
|
||||
- type: Pullable
|
||||
- type: UnarmedCombat
|
||||
range: 2
|
||||
@@ -89,6 +91,8 @@
|
||||
description: Listen to your owner. Don't tank damage. Punch people hard.
|
||||
- type: NameIdentifier
|
||||
group: Holoparasite
|
||||
- type: TypingIndicator
|
||||
proto: holo
|
||||
- type: Sprite
|
||||
layers:
|
||||
- state: tech_base
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
- type: GhostRadio
|
||||
- type: DoAfter
|
||||
- type: Actions
|
||||
- type: TypingIndicator
|
||||
proto: robot
|
||||
- type: Speech
|
||||
speechSounds: Pai
|
||||
# This has to be installed because otherwise they're not "alive",
|
||||
|
||||
19
Resources/Prototypes/typing_indicator.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
- type: typingIndicator
|
||||
id: default
|
||||
typingState: default0
|
||||
|
||||
- type: typingIndicator
|
||||
id: robot
|
||||
typingState: robot0
|
||||
|
||||
- type: typingIndicator
|
||||
id: alien
|
||||
typingState: alien0
|
||||
|
||||
- type: typingIndicator
|
||||
id: guardian
|
||||
typingState: guardian0
|
||||
|
||||
- type: typingIndicator
|
||||
id: holo
|
||||
typingState: holo0
|
||||
BIN
Resources/Textures/Effects/speech.rsi/alien0.png
Normal file
|
After Width: | Height: | Size: 577 B |
BIN
Resources/Textures/Effects/speech.rsi/alien1.png
Normal file
|
After Width: | Height: | Size: 267 B |
BIN
Resources/Textures/Effects/speech.rsi/alien2.png
Normal file
|
After Width: | Height: | Size: 268 B |
BIN
Resources/Textures/Effects/speech.rsi/alienroyal0.png
Normal file
|
After Width: | Height: | Size: 895 B |
BIN
Resources/Textures/Effects/speech.rsi/alienroyal1.png
Normal file
|
After Width: | Height: | Size: 397 B |
BIN
Resources/Textures/Effects/speech.rsi/alienroyal2.png
Normal file
|
After Width: | Height: | Size: 396 B |
BIN
Resources/Textures/Effects/speech.rsi/blob0.png
Normal file
|
After Width: | Height: | Size: 524 B |
BIN
Resources/Textures/Effects/speech.rsi/blob1.png
Normal file
|
After Width: | Height: | Size: 255 B |
BIN
Resources/Textures/Effects/speech.rsi/blob2.png
Normal file
|
After Width: | Height: | Size: 246 B |
BIN
Resources/Textures/Effects/speech.rsi/clock0.png
Normal file
|
After Width: | Height: | Size: 870 B |
BIN
Resources/Textures/Effects/speech.rsi/clock1.png
Normal file
|
After Width: | Height: | Size: 1007 B |
BIN
Resources/Textures/Effects/speech.rsi/clock2.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
Resources/Textures/Effects/speech.rsi/default0.png
Normal file
|
After Width: | Height: | Size: 376 B |
BIN
Resources/Textures/Effects/speech.rsi/default1.png
Normal file
|
After Width: | Height: | Size: 200 B |
BIN
Resources/Textures/Effects/speech.rsi/default2.png
Normal file
|
After Width: | Height: | Size: 190 B |
BIN
Resources/Textures/Effects/speech.rsi/guardian0.png
Normal file
|
After Width: | Height: | Size: 398 B |
BIN
Resources/Textures/Effects/speech.rsi/guardian1.png
Normal file
|
After Width: | Height: | Size: 248 B |
BIN
Resources/Textures/Effects/speech.rsi/guardian2.png
Normal file
|
After Width: | Height: | Size: 235 B |
BIN
Resources/Textures/Effects/speech.rsi/holo0.png
Normal file
|
After Width: | Height: | Size: 506 B |
BIN
Resources/Textures/Effects/speech.rsi/holo1.png
Normal file
|
After Width: | Height: | Size: 251 B |
BIN
Resources/Textures/Effects/speech.rsi/holo2.png
Normal file
|
After Width: | Height: | Size: 241 B |
BIN
Resources/Textures/Effects/speech.rsi/lawyer0.png
Normal file
|
After Width: | Height: | Size: 579 B |
BIN
Resources/Textures/Effects/speech.rsi/lawyer1.png
Normal file
|
After Width: | Height: | Size: 822 B |
BIN
Resources/Textures/Effects/speech.rsi/lawyer2.png
Normal file
|
After Width: | Height: | Size: 851 B |
BIN
Resources/Textures/Effects/speech.rsi/machine0.png
Normal file
|
After Width: | Height: | Size: 342 B |
BIN
Resources/Textures/Effects/speech.rsi/machine1.png
Normal file
|
After Width: | Height: | Size: 209 B |
BIN
Resources/Textures/Effects/speech.rsi/machine2.png
Normal file
|
After Width: | Height: | Size: 186 B |
345
Resources/Textures/Effects/speech.rsi/meta.json
Normal file
@@ -0,0 +1,345 @@
|
||||
{
|
||||
"version": 1,
|
||||
"size": {
|
||||
"x": 64,
|
||||
"y": 64
|
||||
},
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c6e3401f2e7e1e55c57060cdf956a98ef1fefc24",
|
||||
"states": [
|
||||
{
|
||||
"name": "alien0",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.4
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "alien1"
|
||||
},
|
||||
{
|
||||
"name": "alien2"
|
||||
},
|
||||
{
|
||||
"name": "alienroyal0",
|
||||
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.3,
|
||||
0.3,
|
||||
0.3,
|
||||
0.3,
|
||||
0.5
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "alienroyal1",
|
||||
},
|
||||
{
|
||||
"name": "alienroyal2"
|
||||
},
|
||||
{
|
||||
"name": "blob0",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "blob1"
|
||||
},
|
||||
{
|
||||
"name": "blob2"
|
||||
},
|
||||
{
|
||||
"name": "clock0",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.3
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "clock1",
|
||||
"delays": [
|
||||
[
|
||||
0.6,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "clock2",
|
||||
"delays": [
|
||||
[
|
||||
0.6,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "default0",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.3,
|
||||
0.3,
|
||||
0.5
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "default1"
|
||||
},
|
||||
{
|
||||
"name": "default2"
|
||||
},
|
||||
{
|
||||
"name": "guardian0",
|
||||
"delays": [
|
||||
[
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.6
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "guardian1"
|
||||
},
|
||||
{
|
||||
"name": "guardian2"
|
||||
},
|
||||
{
|
||||
"name": "holo0",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "holo1"
|
||||
},
|
||||
{
|
||||
"name": "holo2"
|
||||
},
|
||||
{
|
||||
"name": "lawyer0",
|
||||
"delays": [
|
||||
[
|
||||
0.3,
|
||||
0.3,
|
||||
0.3,
|
||||
0.4
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "lawyer1",
|
||||
"delays": [
|
||||
[
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "lawyer2",
|
||||
"delays": [
|
||||
[
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "machine0",
|
||||
"delays": [
|
||||
[
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "machine1"
|
||||
},
|
||||
{
|
||||
"name": "machine2"
|
||||
},
|
||||
{
|
||||
"name": "robot0",
|
||||
"delays": [
|
||||
[
|
||||
0.4,
|
||||
0.4,
|
||||
0.4,
|
||||
0.4
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "robot1"
|
||||
},
|
||||
{
|
||||
"name": "robot2"
|
||||
},
|
||||
{
|
||||
"name": "slime0",
|
||||
"delays": [
|
||||
[
|
||||
0.1,
|
||||
0.1,
|
||||
0.2,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.5
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "slime1",
|
||||
"delays": [
|
||||
[
|
||||
0.1,
|
||||
0.3,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.6
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "slime2",
|
||||
"delays": [
|
||||
[
|
||||
0.1,
|
||||
0.3,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.6
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "swarmer0",
|
||||
"delays": [
|
||||
[
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "swarmer1",
|
||||
"delays": [
|
||||
[
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "swarmer2",
|
||||
"delays": [
|
||||
[
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "syndibot0",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "syndibot1"
|
||||
},
|
||||
{
|
||||
"name": "syndibot2"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
Resources/Textures/Effects/speech.rsi/robot0.png
Normal file
|
After Width: | Height: | Size: 346 B |
BIN
Resources/Textures/Effects/speech.rsi/robot1.png
Normal file
|
After Width: | Height: | Size: 210 B |
BIN
Resources/Textures/Effects/speech.rsi/robot2.png
Normal file
|
After Width: | Height: | Size: 193 B |
BIN
Resources/Textures/Effects/speech.rsi/slime0.png
Normal file
|
After Width: | Height: | Size: 659 B |
BIN
Resources/Textures/Effects/speech.rsi/slime1.png
Normal file
|
After Width: | Height: | Size: 515 B |
BIN
Resources/Textures/Effects/speech.rsi/slime2.png
Normal file
|
After Width: | Height: | Size: 493 B |
BIN
Resources/Textures/Effects/speech.rsi/swarmer0.png
Normal file
|
After Width: | Height: | Size: 749 B |
BIN
Resources/Textures/Effects/speech.rsi/swarmer1.png
Normal file
|
After Width: | Height: | Size: 880 B |
BIN
Resources/Textures/Effects/speech.rsi/swarmer2.png
Normal file
|
After Width: | Height: | Size: 824 B |
BIN
Resources/Textures/Effects/speech.rsi/syndibot0.png
Normal file
|
After Width: | Height: | Size: 346 B |
BIN
Resources/Textures/Effects/speech.rsi/syndibot1.png
Normal file
|
After Width: | Height: | Size: 210 B |
BIN
Resources/Textures/Effects/speech.rsi/syndibot2.png
Normal file
|
After Width: | Height: | Size: 193 B |