Search the source of every open source package.
15 results
@using System;
@using Sandbox;
@using Sandbox.UI;
@inherits PanelComponent
<root>
<div class="crosshair" style="
position: absolute;
left: @( IsPercentage ? $"{Position.x}%" : Position.x );
top: @( IsPercentage ? $"{Position.y}%" : Position.y );
transform: translate(-50%, -50%);
">
<div class="center-dot-border-wrapper" style="
display: @(CenterDot ? "flex" : "none");
border: @(Outline ? $"{OutlineThickness}px solid rgba(0, 0, 0, {OutlineOpacity})" : "none");
z-index: 100;
">
<div class="center-dot" style="
background-color: rgba(@(Color.r * 255), @(Color.g * 255), @(Color.b * 255), @(CenterDotOpacity));
padding: @(CenterDotThickness)px;
"></div>
</div>
<div class="inner-top-border-wrapper" style="
position: absolute;
top: -@(InnerLinesOffset)px;
left: 50%;
transform: translateX(-50%);
display:@(ShowInnerLines ? "flex" : "none");
border: @(Outline ? $"{OutlineThickness}px solid rgba(0, 0, 0, {OutlineOpacity})" : "none");
">
<div class="inner-line top" style="
background-color: rgba(@(Color.r * 255), @(Color.g * 255), @(Color.b * 255), @(InnerLineOpacity));
padding-left: @(InnerLineThickness)px;
padding-top: @(InnerLineLenght)px;
"></div>
</div>
<div class="inner-bottom-border-wrapper" style="
position: absolute;
bottom: -@(InnerLinesOffset)px;
left: 50%;
transform: translateX(-50%);
display:@(ShowInnerLines ? "flex" : "none");
border: @(Outline ? $"{OutlineThickness}px solid rgba(0, 0, 0, {OutlineOpacity})" : "none");
">
<div class="inner-line bottom" style="
background-color: rgba(@(Color.r * 255), @(Color.g * 255), @(Color.b * 255), @(InnerLineOpacity));
padding-left: @(InnerLineThickness)px;
padding-bottom: @(InnerLineLenght)px;
"></div>
</div>
<div class="inner-left-border-wrapper" style="
position: absolute;
left: -@(InnerLinesOffset)px;
top: 50%;
transform: translateY(-50%);
display:@(ShowInnerLines ? "flex" : "none");
border: @(Outline ? $"{OutlineThickness}px solid rgba(0, 0, 0, {OutlineOpacity})" : "none");
">
<div class="inner-line left" style="
background-color: rgba(@(Color.r * 255), @(Color.g * 255), @(Color.b * 255), @(InnerLineOpacity));
padding-top: @(InnerLineThickness)px;
padding-left: @(InnerLineLenght)px;
"></div>
</div>
<div class="inner-right-border-wrapper" style="
position: absolute;
right: -@(InnerLinesOffset)px;
top: 50%;
transform: translateY(-50%);
display: @(ShowInnerLines ? "flex" : "none");
border: @(Outline ? $"{OutlineThickness}px solid rgba(0, 0, 0, {OutlineOpacity})" : "none");
">
<div class="inner-line right" style="
background-color: rgba(@(Color.r * 255), @(Color.g * 255), @(Color.b * 255), @(InnerLineOpacity));
padding-top: @(InnerLineThickness)px;
padding-right: @(InnerLineLenght)px;
"></div>
</div>
<div class="outer-top-border-wrapper" style="
position: absolute;
top: -@(OuterLineOffset)px;
left: 50%;
transform: translateX(-50%);
display: @(ShowOuterLines ? "flex" : "none");
border: @(Outline ? $"{OutlineThickness}px solid rgba(0, 0, 0, {OutlineOpacity})" : "none");
">
<div class="outer-line top" style="
background-color: rgba(@(Color.r * 255), @(Color.g * 255), @(Color.b * 255), @(OuterLineOpacity));
padding-left: @(OuterLineThickness)px;
padding-top: @(OuterLineLenght)px;
"></div>
</div>
<div class="outer-bottom-border-wrapper" style="
position: absolute;
bottom: -@(OuterLineOffset)px;
left: 50%;
transform: translateX(-50%);
display: @(ShowOuterLines ? "flex" : "none");
border: @(Outline ? $"{OutlineThickness}px solid rgba(0, 0, 0, {OutlineOpacity})" : "none");
">
<div class="outer-line bottom" style="
background-color: rgba(@(Color.r * 255), @(Color.g * 255), @(Color.b * 255), @(OuterLineOpacity));
padding-left: @(OuterLineThickness)px;
padding-bottom: @(OuterLineLenght)px;
"></div>
</div>
<div class="outer-left-border-wrapper" style="
position: absolute;
left: -@(OuterLineOffset)px;
top: 50%;
transform: translateY(-50%);
display: @(ShowOuterLines ? "flex" : "none");
border: @(Outline ? $"{OutlineThickness}px solid rgba(0, 0, 0, {OutlineOpacity})" : "none");
">
<div class="outer-line left" style="
background-color: rgba(@(Color.r * 255), @(Color.g * 255), @(Color.b * 255), @(OuterLineOpacity));
padding-top: @(OuterLineThickness)px;
padding-left: @(OuterLineLenght)px;
"></div>
</div>
<div class="outer-right-border-wrapper" style="
position: absolute;
right: -@(OuterLineOffset)px;
top: 50%;
transform: translateY(-50%);
display: @(ShowOuterLines ? "flex" : "none");
border: @(Outline ? $"{OutlineThickness}px solid rgba(0, 0, 0, {OutlineOpacity})" : "none");
">
<div class="outer-line right" style="
background-color: rgba(@(Color.r * 255), @(Color.g * 255), @(Color.b * 255), @(OuterLineOpacity));
padding-top: @(OuterLineThickness)px;
padding-right: @(OuterLineLenght)px;
"></div>
</div>
</div>
</root>
@code
{
// Position properties
[Property]
[Category("Position")] public bool IsPercentage { get; set; } = true;
[Property]
[Category("Position")] public Vector2 Position { get; set; } = new Vector2(50, 50);
// Crosshair properties
[Property]
[Category("Crosshair")] public Color Color { get; set; } = Color.FromRgb(0x2EFF00);
[Property]
[Category("Crosshair")] public bool Outline { get; set; } = true;
[Property]
[Range(0f, 1f, 0.001f)]
[Category("Crosshair")] public float OutlineOpacity { get; set; } = 1f;
[Property]
[Range(1, 10, 1)]
[Category("Crosshair")] public int OutlineThickness { get; set; } = 2;
[Property]
[Category("Crosshair")] public bool CenterDot { get; set; } = true;
[Property]
[Range(0f, 1f, 0.001f)]
[Category("Crosshair")] public float CenterDotOpacity { get; set; } = 1f;
[Property]
[Range(1, 10, 1)]
[Category("Crosshair")] public int CenterDotThickness { get; set; } = 1;
// Inner Lines properties
[Property]
[Category("Inner Lines")] public bool ShowInnerLines { get; set; } = true;
[Property]
[Range(0f, 1f, 0.001f)]
[Category("Inner Lines")] public float InnerLineOpacity { get; set; } = 1f;
[Property]
[Range(0, 20, 1)]
[Category("Inner Lines")] public int InnerLineLenght { get; set; } = 10;
[Property]
[Range(0, 10, 1)]
[Category("Inner Lines")] public int InnerLineThickness { get; set; } = 1;
[Property]
[Range(0, 20, 1)]
[Category("Inner Lines")] public int InnerLinesOffset { get; set; } = 2;
// Outer Lines properties
[Property]
[Category("Outer Lines")] public bool ShowOuterLines { get; set; } = false;
[Property]
[Range(0f, 1f, 0.001f)]
[Category("Outer Lines")] public float OuterLineOpacity { get; set; } = 0.5f;
[Property]
[Range(0, 20, 1)]
[Category("Outer Lines")] public int OuterLineLenght { get; set; }
[Property]
[Range(0, 10, 1)]
[Category("Outer Lines")] public int OuterLineThickness { get; set; }
[Property]
[Range(0, 20, 1)]
[Category("Outer Lines")] public int OuterLineOffset { get; set; }
// Code properties
[Property]
[Category("Code")] public string Code { get; set; }
// Member variables
private string _cachedCode = null;
public string EncodeCrosshairParameters()
{
// Convert boolean values to 1 or 0
string EncodeBool(bool value) => value ? "1" : "0";
// Convert float values to a shortened string representation (up to 2 decimal places)
string EncodeFloat(float value) => Math.Round(value, 2).ToString("0.##");
// Convert integers directly to string
string EncodeInt(int value) => value.ToString();
// Concatenate all parameters into a shortened format
var parameters = $"{EncodeBool(IsPercentage)}," +
$"{EncodeFloat(Position.x)},{EncodeFloat(Position.y)}," +
$"{EncodeFloat(Color.r)},{EncodeFloat(Color.g)},{EncodeFloat(Color.b)}," +
$"{EncodeBool(Outline)},{EncodeFloat(OutlineOpacity)},{EncodeInt(OutlineThickness)}," +
$"{EncodeBool(CenterDot)},{EncodeFloat(CenterDotOpacity)},{EncodeInt(CenterDotThickness)}," +
$"{EncodeBool(ShowInnerLines)},{EncodeFloat(InnerLineOpacity)},{EncodeInt(InnerLineLenght)}," +
$"{EncodeInt(InnerLineThickness)},{EncodeInt(InnerLinesOffset)}," +
$"{EncodeBool(ShowOuterLines)},{EncodeFloat(OuterLineOpacity)},{EncodeInt(OuterLineLenght)}," +
$"{EncodeInt(OuterLineThickness)},{EncodeInt(OuterLineOffset)}";
return parameters;
}
public void DecodeCrosshairParameters(string encodedString)
{
var parameters = encodedString.Split(',');
// Convert "1" or "0" back to boolean
bool DecodeBool(string value) => value == "1";
// Convert string back to float
float DecodeFloat(string value) => float.Parse(value);
// Convert string back to int
int DecodeInt(string value) => int.Parse(value);
// Assign the decoded values back to the properties
IsPercentage = DecodeBool(parameters[0]);
Position = new Vector2(DecodeFloat(parameters[1]), DecodeFloat(parameters[2]));
Color = new Color(DecodeFloat(parameters[3]), DecodeFloat(parameters[4]), DecodeFloat(parameters[5]));
Outline = DecodeBool(parameters[6]);
OutlineOpacity = DecodeFloat(parameters[7]);
OutlineThickness = DecodeInt(parameters[8]);
CenterDot = DecodeBool(parameters[9]);
CenterDotOpacity = DecodeFloat(parameters[10]);
CenterDotThickness = DecodeInt(parameters[11]);
ShowInnerLines = DecodeBool(parameters[12]);
InnerLineOpacity = DecodeFloat(parameters[13]);
InnerLineLenght = DecodeInt(parameters[14]);
InnerLineThickness = DecodeInt(parameters[15]);
InnerLinesOffset = DecodeInt(parameters[16]);
ShowOuterLines = DecodeBool(parameters[17]);
OuterLineOpacity = DecodeFloat(parameters[18]);
OuterLineLenght = DecodeInt(parameters[19]);
OuterLineThickness = DecodeInt(parameters[20]);
OuterLineOffset = DecodeInt(parameters[21]);
}
protected override void OnStart()
{
if (Code == null)
{
Code = EncodeCrosshairParameters();
}
DecodeCrosshairParameters(Code);
_cachedCode = Code;
}
protected override void OnUpdate()
{
if (Code != _cachedCode)
{
DecodeCrosshairParameters(Code);
}
Code = EncodeCrosshairParameters();
_cachedCode = Code;
}
/// <summary>
/// the hash determines if the system should be rebuilt. If it changes, it will be rebuilt
/// </summary>
protected override int BuildHash()
{
var positionHash = System.HashCode.Combine(
IsPercentage,
Position
);
var crosshairHash = System.HashCode.Combine(
Color,
Outline,
OutlineOpacity,
OutlineThickness,
CenterDot,
CenterDotOpacity,
CenterDotThickness
);
var innerLinesHash = System.HashCode.Combine(
ShowInnerLines,
InnerLineOpacity,
InnerLineLenght,
InnerLineThickness,
InnerLinesOffset
);
var outerLinesHash = System.HashCode.Combine(
ShowOuterLines,
OuterLineOpacity,
OuterLineLenght,
OuterLineThickness,
OuterLineOffset
);
var codeHash = Code.GetHashCode();
return System.HashCode.Combine(
positionHash,
crosshairHash,
innerLinesHash,
outerLinesHash,
codeHash
);
}
}
@using System
@using System.Threading.Tasks
@using Sandbox
@using Sandbox.UI
@inherits WebPanel
<style>
/* .scss files don't work in libraries. https://github.com/Facepunch/sbox-issues/issues/4813 */
prompt {
width: 100%;
pointer-events: all;
}
</style>
@code {
public readonly TaskCompletionSource<bool> Purchased;
public Prompt( string URL )
{
Url = URL;
Purchased = new TaskCompletionSource<bool>();
if ( !Game.ActiveScene.Components.TryGet( out ScreenPanel panel ) )
{
panel = Game.ActiveScene.Components.Create<ScreenPanel>();
panel.GetPanel().ElementName = "monetization";
panel.ZIndex = int.MaxValue;
}
Parent = panel.GetPanel();
}
public override void OnDeleted()
{
if ( !Game.ActiveScene.Components.TryGet( out ScreenPanel panel ) )
{
return;
}
if ( panel.GetPanel().HasChildren )
{
return;
}
panel.Destroy();
}
protected override void OnAfterTreeRender( bool firstTime )
{
// This was the only way I could think of to get the
// Steam browser to send data back data to the game.
// If you have a better way let me know.
switch ( Surface.PageTitle )
{
case "finished": Delete();
break;
case "purchased": Purchased.TrySetResult( true );
break;
case "cancelled": Purchased.TrySetResult( false );
break;
}
}
protected override int BuildHash() => HashCode.Combine( Surface.PageTitle );
}
@using Sandbox;
@using Sandbox.Audio;
@using Sandbox.UI;
@using Sandbox.Services;
@using System;
@using System.Collections.Generic;
@using System.Linq;
@inherits PanelComponent
<root class="menu">
<div class="container">
<div class="title">
<span>Options</span>
</div>
@if (activeTab != null)
{
<div class="content tabs-container">
<div class="tabs-group">
@foreach (var tab in tabs)
{
if (tab == null)
continue;
<div class="button red @(tab == activeTab ? "active" : "inactive")" onclick="@(() => activeTab = tab)">
@tab.tabName
</div>
}
</div>
</div>
<div class="content tab-content" CanDragScroll="false">
<div class="table">
@foreach (var group in activeTab.groups)
{
<div class="row title">
<div class="label">@group.groupName</div>
</div>
foreach (var element in group.elements)
{
var elementType = element.GetType();
<div class="row setting">
<div class="column key">
<div class="label">@element.displayName:</div>
</div>
@if (element is UIToggle)
{
var toggleElement = (UIToggle)element;
<div class="column value">
<div class="buttons">
<div class="button @GetToggleColor(true, toggleElement.getter())" onclick="@(() => toggleElement.setter(false))">Off</div>
<div class="button @GetToggleColor(false, toggleElement.getter())" onclick="@(() => toggleElement.setter(true))">On</div>
</div>
</div>
}
@if (element is UISlider)
{
var sliderElement = (UISlider)element;
<div class="column value">
<div class="slider">
<SliderControl Value="@(sliderElement.getter())" OnValueChanged="@(sliderElement.setter)" Min="@(sliderElement.min)" Max="@(sliderElement.max)" Step="@(sliderElement.step)"></SliderControl>
</div>
</div>
}
@if (element is UICyclerBase)
{
var cyclerElement = (UICyclerBase)element;
<div class="column value">
<div class="cycling-selector">
<div class="cycling-controls">
<button class="arrow left" onclick="@(() => cyclerElement.CycleLeft())">‹</button>
<span class="value" id="current-value">@cyclerElement.onGet()</span>
<button class="arrow right" onclick="@(() => cyclerElement.CycleRight())">›</button>
</div>
</div>
</div>
}
</div>
}
}
</div>
</div>
}
<div class="menu buttons">
<div class="button red" onclick="@(() => ButtonClose())">
Close
</div>
</div>
</div>
</root>
@code
{
public static string GetToggleColor(bool isOff, bool value)
{
if (isOff)
{
return value ? "gray" : "red";
}
return value ? "green" : "gray";
}
public List<UITab> tabs { get; set; } = new List<UITab>();
public UITab activeTab { get; set; }
protected override void OnAwake()
{
base.OnAwake();
var sortedValues = EasySaveNonGenericBase.typeToInst.Values.OrderBy(v => (v.uiTab != null) ? v.uiTab.order : 99999).ToList();
foreach (var sortedValue in sortedValues)
{
if (sortedValue == null)
continue;
tabs.Add(sortedValue.uiTab);
}
if (tabs != null && tabs.Count() > 0)
{
activeTab = tabs[0];
}
}
protected override void OnUpdate()
{
base.OnUpdate();
if (Input.EscapePressed)
{
Input.EscapePressed = false;
ButtonClose();
}
}
[ConCmd("open_options")]
public static void OpenOptions()
{
var inst = Game.ActiveScene.Components.Get<OptionsScreen>(true);
if (inst != null)
{
inst.Enabled = true;
}
else
{
var optionsScreenGO = Game.ActiveScene.CreateObject();
var screenPanel = optionsScreenGO.Components.Create<ScreenPanel>();
var optionsScreen = optionsScreenGO.Components.Create<OptionsScreen>();
}
}
void ButtonClose()
{
var soundHandle = Sound.Play("ui.navigate.back");
soundHandle.TargetMixer = Mixer.FindMixerByName("UI");
Close();
}
public void Close()
{
this.Enabled = false;
}
public bool wasMouseVisible { get; set; } = false;
protected override void OnEnabled()
{
wasMouseVisible = Mouse.Visible;
Mouse.Visible = true;
}
protected override void OnDisabled()
{
EasySaveNonGenericBase.SaveAll();
Mouse.Visible = wasMouseVisible;
}
/// <summary>
/// update every second
/// </summary>
protected override int BuildHash() => System.HashCode.Combine(RealTime.Now.CeilToInt());
}
@using Sandbox;
@inherits PanelComponent
@if (InSequence)
{
<root>
<div class="Main">
<div style="opacity: @Opacity">
<img src="@CurrentEntry.TexturePath" style="transform: scale(@Scale);">
</div>
</div>
</root>
}
@using Sandbox.UI;
@namespace Duccsoft
@inherits Panel
<root class=@OverlayClass>
<image id="spinner" src="ui/img/BufferingCircle.png"/>
<div id="bottomBar">
<div class="button symbol" onclick=@TogglePause>@PlayButtonIcon</div>
<div id="timecodeArea" class=@TimecodeAreaClass>
<div class="progress time">@ProgressText</div>
<div class="progress center">/</div>
<div class="progress time right">@DurationText</div>
</div>
<SliderControl id="progressBar" Min=@(0) Max=@DurationSeconds
Value:bind=@PlaybackTime OnValueChanged=@ProgressBarChanged ShowValueTooltip=@false/>
<div class="button symbol" onclick=@ToggleMute>@VolumeButtonIcon</div>
</div>
</root>
@using System.Collections.Generic;
@using System.Linq;
@using Sandbox;
@using Sandbox.UI;
@namespace TwitchAPI.Examples
<root>
<div class="username">
<div class="badges">
@foreach (var badge in ChatMessage.User.Badges)
{
<div class="badge @badge" />
}
</div>
@if (HasAvatarImages)
{
<img class="avatar" [email protected]() />
}
<label class="name" style="color: @ChatMessage.User.Color.Hex">@ChatMessage.Username</label>
<label>:</label>
</div>
<div class="message">
@foreach (var fragment in Fragments)
{
if (fragment.Emote is not null)
{
<img class="emote" src="@fragment.Emote" />
}
else
{
<label>@fragment.Text</label>
}
}
</div>
</root>
@code
{
public TwitchChatMessage ChatMessage { get; set; }
public float Lifetime { get; set; } = 10f;
public float FadeTime { get; set; } = 0.5f;
public bool HasAvatarImages { get; set; } = false;
List<MessageFragment> Fragments { get; set; } = new();
TimeSince TimeSinceCreated = 0f;
record MessageFragment(string Text, string Emote);
protected override void OnParametersSet()
{
base.OnParametersSet();
Fragments.Clear();
var text = ChatMessage.Message;
var emotes = ChatMessage.Emotes;
var currentEmote = emotes.FirstOrDefault();
var currentIndex = 0;
while (currentEmote is not null)
{
var textPrior = text.Substring(currentIndex, currentEmote.StartingCharacter - currentIndex);
if (!string.IsNullOrWhiteSpace(textPrior))
{
foreach (var word in textPrior.Split(' '))
{
if (string.IsNullOrWhiteSpace(word)) continue;
Fragments.Add(new MessageFragment(word, null));
}
}
var emoteText = text.Substring(currentEmote.StartingCharacter, currentEmote.EndingCharacter - currentEmote.StartingCharacter + 1);
Fragments.Add(new MessageFragment(emoteText, currentEmote.GetImageUrl()));
currentIndex = currentEmote.EndingCharacter + 1;
currentEmote = emotes.FirstOrDefault(e => e.StartingCharacter >= currentIndex);
}
var textAfter = text.Substring(currentIndex);
if (!string.IsNullOrWhiteSpace(textAfter))
{
foreach (var word in textAfter.Split(' '))
{
if (string.IsNullOrWhiteSpace(word)) continue;
Fragments.Add(new MessageFragment(word, null));
}
}
}
public override void Tick()
{
if (TimeSinceCreated >= Lifetime)
{
var opacity = 1f - ((TimeSinceCreated - Lifetime) / FadeTime);
if (opacity <= 0f)
{
Delete();
}
else
{
Style.Opacity = opacity;
}
}
}
}@using System;
@using Sandbox;
@using Sandbox.UI;
@inherits PanelComponent
<root>
<div class="crosshair" style="
position: absolute;
left: @( IsPercentage ? $"{Position.x}%" : Position.x );
top: @( IsPercentage ? $"{Position.y}%" : Position.y );
transform: translate(-50%, -50%);
">
<div class="center-dot-border-wrapper" style="
display: @(CenterDot ? "flex" : "none");
border: @(Outline ? $"{OutlineThickness}px solid rgba(0, 0, 0, {OutlineOpacity})" : "none");
z-index: 100;
">
<div class="center-dot" style="
background-color: rgba(@(Color.r * 255), @(Color.g * 255), @(Color.b * 255), @(CenterDotOpacity));
padding: @(CenterDotThickness)px;
"></div>
</div>
<div class="inner-top-border-wrapper" style="
position: absolute;
top: -@(InnerLinesOffset)px;
left: 50%;
transform: translateX(-50%);
display:@(ShowInnerLines ? "flex" : "none");
border: @(Outline ? $"{OutlineThickness}px solid rgba(0, 0, 0, {OutlineOpacity})" : "none");
">
<div class="inner-line top" style="
background-color: rgba(@(Color.r * 255), @(Color.g * 255), @(Color.b * 255), @(InnerLineOpacity));
padding-left: @(InnerLineThickness)px;
padding-top: @(InnerLineLenght)px;
"></div>
</div>
<div class="inner-bottom-border-wrapper" style="
position: absolute;
bottom: -@(InnerLinesOffset)px;
left: 50%;
transform: translateX(-50%);
display:@(ShowInnerLines ? "flex" : "none");
border: @(Outline ? $"{OutlineThickness}px solid rgba(0, 0, 0, {OutlineOpacity})" : "none");
">
<div class="inner-line bottom" style="
background-color: rgba(@(Color.r * 255), @(Color.g * 255), @(Color.b * 255), @(InnerLineOpacity));
padding-left: @(InnerLineThickness)px;
padding-bottom: @(InnerLineLenght)px;
"></div>
</div>
<div class="inner-left-border-wrapper" style="
position: absolute;
left: -@(InnerLinesOffset)px;
top: 50%;
transform: translateY(-50%);
display:@(ShowInnerLines ? "flex" : "none");
border: @(Outline ? $"{OutlineThickness}px solid rgba(0, 0, 0, {OutlineOpacity})" : "none");
">
<div class="inner-line left" style="
background-color: rgba(@(Color.r * 255), @(Color.g * 255), @(Color.b * 255), @(InnerLineOpacity));
padding-top: @(InnerLineThickness)px;
padding-left: @(InnerLineLenght)px;
"></div>
</div>
<div class="inner-right-border-wrapper" style="
position: absolute;
right: -@(InnerLinesOffset)px;
top: 50%;
transform: translateY(-50%);
display: @(ShowInnerLines ? "flex" : "none");
border: @(Outline ? $"{OutlineThickness}px solid rgba(0, 0, 0, {OutlineOpacity})" : "none");
">
<div class="inner-line right" style="
background-color: rgba(@(Color.r * 255), @(Color.g * 255), @(Color.b * 255), @(InnerLineOpacity));
padding-top: @(InnerLineThickness)px;
padding-right: @(InnerLineLenght)px;
"></div>
</div>
<div class="outer-top-border-wrapper" style="
position: absolute;
top: -@(OuterLineOffset)px;
left: 50%;
transform: translateX(-50%);
display: @(ShowOuterLines ? "flex" : "none");
border: @(Outline ? $"{OutlineThickness}px solid rgba(0, 0, 0, {OutlineOpacity})" : "none");
">
<div class="outer-line top" style="
background-color: rgba(@(Color.r * 255), @(Color.g * 255), @(Color.b * 255), @(OuterLineOpacity));
padding-left: @(OuterLineThickness)px;
padding-top: @(OuterLineLenght)px;
"></div>
</div>
<div class="outer-bottom-border-wrapper" style="
position: absolute;
bottom: -@(OuterLineOffset)px;
left: 50%;
transform: translateX(-50%);
display: @(ShowOuterLines ? "flex" : "none");
border: @(Outline ? $"{OutlineThickness}px solid rgba(0, 0, 0, {OutlineOpacity})" : "none");
">
<div class="outer-line bottom" style="
background-color: rgba(@(Color.r * 255), @(Color.g * 255), @(Color.b * 255), @(OuterLineOpacity));
padding-left: @(OuterLineThickness)px;
padding-bottom: @(OuterLineLenght)px;
"></div>
</div>
<div class="outer-left-border-wrapper" style="
position: absolute;
left: -@(OuterLineOffset)px;
top: 50%;
transform: translateY(-50%);
display: @(ShowOuterLines ? "flex" : "none");
border: @(Outline ? $"{OutlineThickness}px solid rgba(0, 0, 0, {OutlineOpacity})" : "none");
">
<div class="outer-line left" style="
background-color: rgba(@(Color.r * 255), @(Color.g * 255), @(Color.b * 255), @(OuterLineOpacity));
padding-top: @(OuterLineThickness)px;
padding-left: @(OuterLineLenght)px;
"></div>
</div>
<div class="outer-right-border-wrapper" style="
position: absolute;
right: -@(OuterLineOffset)px;
top: 50%;
transform: translateY(-50%);
display: @(ShowOuterLines ? "flex" : "none");
border: @(Outline ? $"{OutlineThickness}px solid rgba(0, 0, 0, {OutlineOpacity})" : "none");
">
<div class="outer-line right" style="
background-color: rgba(@(Color.r * 255), @(Color.g * 255), @(Color.b * 255), @(OuterLineOpacity));
padding-top: @(OuterLineThickness)px;
padding-right: @(OuterLineLenght)px;
"></div>
</div>
</div>
</root>
@code
{
// Position properties
[Property]
[Category("Position")] public bool IsPercentage { get; set; } = true;
[Property]
[Category("Position")] public Vector2 Position { get; set; } = new Vector2(50, 50);
// Crosshair properties
[Property]
[Category("Crosshair")] public Color Color { get; set; } = Color.FromRgb(0x2EFF00);
[Property]
[Category("Crosshair")] public bool Outline { get; set; } = true;
[Property]
[Range(0f, 1f, 0.001f)]
[Category("Crosshair")] public float OutlineOpacity { get; set; } = 1f;
[Property]
[Range(1, 10, 1)]
[Category("Crosshair")] public int OutlineThickness { get; set; } = 2;
[Property]
[Category("Crosshair")] public bool CenterDot { get; set; } = true;
[Property]
[Range(0f, 1f, 0.001f)]
[Category("Crosshair")] public float CenterDotOpacity { get; set; } = 1f;
[Property]
[Range(1, 10, 1)]
[Category("Crosshair")] public int CenterDotThickness { get; set; } = 1;
// Inner Lines properties
[Property]
[Category("Inner Lines")] public bool ShowInnerLines { get; set; } = true;
[Property]
[Range(0f, 1f, 0.001f)]
[Category("Inner Lines")] public float InnerLineOpacity { get; set; } = 1f;
[Property]
[Range(0, 20, 1)]
[Category("Inner Lines")] public int InnerLineLenght { get; set; } = 10;
[Property]
[Range(0, 10, 1)]
[Category("Inner Lines")] public int InnerLineThickness { get; set; } = 1;
[Property]
[Range(0, 20, 1)]
[Category("Inner Lines")] public int InnerLinesOffset { get; set; } = 2;
// Outer Lines properties
[Property]
[Category("Outer Lines")] public bool ShowOuterLines { get; set; } = false;
[Property]
[Range(0f, 1f, 0.001f)]
[Category("Outer Lines")] public float OuterLineOpacity { get; set; } = 0.5f;
[Property]
[Range(0, 20, 1)]
[Category("Outer Lines")] public int OuterLineLenght { get; set; }
[Property]
[Range(0, 10, 1)]
[Category("Outer Lines")] public int OuterLineThickness { get; set; }
[Property]
[Range(0, 20, 1)]
[Category("Outer Lines")] public int OuterLineOffset { get; set; }
// Code properties
[Property]
[Category("Code")] public string Code { get; set; }
// Member variables
private string _cachedCode = null;
public string EncodeCrosshairParameters()
{
// Convert boolean values to 1 or 0
string EncodeBool(bool value) => value ? "1" : "0";
// Convert float values to a shortened string representation (up to 2 decimal places)
string EncodeFloat(float value) => Math.Round(value, 2).ToString("0.##");
// Convert integers directly to string
string EncodeInt(int value) => value.ToString();
// Concatenate all parameters into a shortened format
var parameters = $"{EncodeBool(IsPercentage)}," +
$"{EncodeFloat(Position.x)},{EncodeFloat(Position.y)}," +
$"{EncodeFloat(Color.r)},{EncodeFloat(Color.g)},{EncodeFloat(Color.b)}," +
$"{EncodeBool(Outline)},{EncodeFloat(OutlineOpacity)},{EncodeInt(OutlineThickness)}," +
$"{EncodeBool(CenterDot)},{EncodeFloat(CenterDotOpacity)},{EncodeInt(CenterDotThickness)}," +
$"{EncodeBool(ShowInnerLines)},{EncodeFloat(InnerLineOpacity)},{EncodeInt(InnerLineLenght)}," +
$"{EncodeInt(InnerLineThickness)},{EncodeInt(InnerLinesOffset)}," +
$"{EncodeBool(ShowOuterLines)},{EncodeFloat(OuterLineOpacity)},{EncodeInt(OuterLineLenght)}," +
$"{EncodeInt(OuterLineThickness)},{EncodeInt(OuterLineOffset)}";
return parameters;
}
public void DecodeCrosshairParameters(string encodedString)
{
var parameters = encodedString.Split(',');
// Convert "1" or "0" back to boolean
bool DecodeBool(string value) => value == "1";
// Convert string back to float
float DecodeFloat(string value) => float.Parse(value);
// Convert string back to int
int DecodeInt(string value) => int.Parse(value);
// Assign the decoded values back to the properties
IsPercentage = DecodeBool(parameters[0]);
Position = new Vector2(DecodeFloat(parameters[1]), DecodeFloat(parameters[2]));
Color = new Color(DecodeFloat(parameters[3]), DecodeFloat(parameters[4]), DecodeFloat(parameters[5]));
Outline = DecodeBool(parameters[6]);
OutlineOpacity = DecodeFloat(parameters[7]);
OutlineThickness = DecodeInt(parameters[8]);
CenterDot = DecodeBool(parameters[9]);
CenterDotOpacity = DecodeFloat(parameters[10]);
CenterDotThickness = DecodeInt(parameters[11]);
ShowInnerLines = DecodeBool(parameters[12]);
InnerLineOpacity = DecodeFloat(parameters[13]);
InnerLineLenght = DecodeInt(parameters[14]);
InnerLineThickness = DecodeInt(parameters[15]);
InnerLinesOffset = DecodeInt(parameters[16]);
ShowOuterLines = DecodeBool(parameters[17]);
OuterLineOpacity = DecodeFloat(parameters[18]);
OuterLineLenght = DecodeInt(parameters[19]);
OuterLineThickness = DecodeInt(parameters[20]);
OuterLineOffset = DecodeInt(parameters[21]);
}
protected override void OnStart()
{
if (Code == null)
{
Code = EncodeCrosshairParameters();
}
DecodeCrosshairParameters(Code);
_cachedCode = Code;
}
protected override void OnUpdate()
{
if (Code != _cachedCode)
{
DecodeCrosshairParameters(Code);
}
Code = EncodeCrosshairParameters();
_cachedCode = Code;
}
/// <summary>
/// the hash determines if the system should be rebuilt. If it changes, it will be rebuilt
/// </summary>
protected override int BuildHash()
{
var positionHash = System.HashCode.Combine(
IsPercentage,
Position
);
var crosshairHash = System.HashCode.Combine(
Color,
Outline,
OutlineOpacity,
OutlineThickness,
CenterDot,
CenterDotOpacity,
CenterDotThickness
);
var innerLinesHash = System.HashCode.Combine(
ShowInnerLines,
InnerLineOpacity,
InnerLineLenght,
InnerLineThickness,
InnerLinesOffset
);
var outerLinesHash = System.HashCode.Combine(
ShowOuterLines,
OuterLineOpacity,
OuterLineLenght,
OuterLineThickness,
OuterLineOffset
);
var codeHash = Code.GetHashCode();
return System.HashCode.Combine(
positionHash,
crosshairHash,
innerLinesHash,
outerLinesHash,
codeHash
);
}
}
@using System.Collections.Generic;
@using System.Linq;
@using Sandbox;
@using Sandbox.UI;
@namespace TwitchAPI.Examples
<root>
<div class="username">
<div class="badges">
@foreach (var badge in ChatMessage.User.Badges)
{
<div class="badge @badge" />
}
</div>
@if (HasAvatarImages)
{
<img class="avatar" [email protected]() />
}
<label class="name" style="color: @ChatMessage.User.Color.Hex">@ChatMessage.Username</label>
<label>:</label>
</div>
<div class="message">
@foreach (var fragment in Fragments)
{
if (fragment.Emote is not null)
{
<img class="emote" src="@fragment.Emote" />
}
else
{
<label>@fragment.Text</label>
}
}
</div>
</root>
@code
{
public TwitchChatMessage ChatMessage { get; set; }
public float Lifetime { get; set; } = 10f;
public float FadeTime { get; set; } = 0.5f;
public bool HasAvatarImages { get; set; } = false;
List<MessageFragment> Fragments { get; set; } = new();
TimeSince TimeSinceCreated = 0f;
record MessageFragment(string Text, string Emote);
protected override void OnParametersSet()
{
base.OnParametersSet();
Fragments.Clear();
var text = ChatMessage.Message;
var emotes = ChatMessage.Emotes;
var currentEmote = emotes.FirstOrDefault();
var currentIndex = 0;
while (currentEmote is not null)
{
var textPrior = text.Substring(currentIndex, currentEmote.StartingCharacter - currentIndex);
if (!string.IsNullOrWhiteSpace(textPrior))
{
foreach (var word in textPrior.Split(' '))
{
if (string.IsNullOrWhiteSpace(word)) continue;
Fragments.Add(new MessageFragment(word, null));
}
}
var emoteText = text.Substring(currentEmote.StartingCharacter, currentEmote.EndingCharacter - currentEmote.StartingCharacter + 1);
Fragments.Add(new MessageFragment(emoteText, currentEmote.GetImageUrl()));
currentIndex = currentEmote.EndingCharacter + 1;
currentEmote = emotes.FirstOrDefault(e => e.StartingCharacter >= currentIndex);
}
var textAfter = text.Substring(currentIndex);
if (!string.IsNullOrWhiteSpace(textAfter))
{
foreach (var word in textAfter.Split(' '))
{
if (string.IsNullOrWhiteSpace(word)) continue;
Fragments.Add(new MessageFragment(word, null));
}
}
}
public override void Tick()
{
if (TimeSinceCreated >= Lifetime)
{
var opacity = 1f - ((TimeSinceCreated - Lifetime) / FadeTime);
if (opacity <= 0f)
{
Delete();
}
else
{
Style.Opacity = opacity;
}
}
}
}@using Sandbox;
@inherits PanelComponent
@if (InSequence)
{
<root>
<div class="Main">
<div style="opacity: @Opacity">
<img src="@CurrentEntry.TexturePath" style="transform: scale(@Scale);">
</div>
</div>
</root>
}
@using System
@using System.Threading.Tasks
@using Sandbox
@using Sandbox.UI
@inherits WebPanel
<style>
/* .scss files don't work in libraries. https://github.com/Facepunch/sbox-issues/issues/4813 */
prompt {
width: 100%;
pointer-events: all;
}
</style>
@code {
public readonly TaskCompletionSource<bool> Purchased;
public Prompt( string URL )
{
Url = URL;
Purchased = new TaskCompletionSource<bool>();
if ( !Game.ActiveScene.Components.TryGet( out ScreenPanel panel ) )
{
panel = Game.ActiveScene.Components.Create<ScreenPanel>();
panel.GetPanel().ElementName = "monetization";
panel.ZIndex = int.MaxValue;
}
Parent = panel.GetPanel();
}
public override void OnDeleted()
{
if ( !Game.ActiveScene.Components.TryGet( out ScreenPanel panel ) )
{
return;
}
if ( panel.GetPanel().HasChildren )
{
return;
}
panel.Destroy();
}
protected override void OnAfterTreeRender( bool firstTime )
{
// This was the only way I could think of to get the
// Steam browser to send data back data to the game.
// If you have a better way let me know.
switch ( Surface.PageTitle )
{
case "finished": Delete();
break;
case "purchased": Purchased.TrySetResult( true );
break;
case "cancelled": Purchased.TrySetResult( false );
break;
}
}
protected override int BuildHash() => HashCode.Combine( Surface.PageTitle );
}
@using Sandbox.UI;
@namespace Duccsoft
@inherits Panel
<root class=@OverlayClass>
<image id="spinner" src="ui/img/BufferingCircle.png"/>
<div id="bottomBar">
<div class="button symbol" onclick=@TogglePause>@PlayButtonIcon</div>
<div id="timecodeArea" class=@TimecodeAreaClass>
<div class="progress time">@ProgressText</div>
<div class="progress center">/</div>
<div class="progress time right">@DurationText</div>
</div>
<SliderControl id="progressBar" Min=@(0) Max=@DurationSeconds
Value:bind=@PlaybackTime OnValueChanged=@ProgressBarChanged ShowValueTooltip=@false/>
<div class="button symbol" onclick=@ToggleMute>@VolumeButtonIcon</div>
</div>
</root>
@using Sandbox.UI;
@namespace Duccsoft
@inherits Panel
<root class="video-texture">
@if ( ShowControls )
{
<VideoControlOverlay VideoPanel=@this AutoHide=@AutoHideControls AutoHideDelay=@AutoHideDelay/>
}
</root>
@using Sandbox;
@using Sandbox.UI;
@inherits PanelComponent
@namespace TwitchAPI.Examples
<root>
</root>
@code
{
/// <summary>
/// The username of the Twitch Chat to connect to.
/// </summary>
[Property] string Username { get; set; }
/// <summary>
/// Whether or not to include the user's avatar images in the chat.
/// </summary>
[Property] bool HasAvatarImages { get; set; } = false;
/// <summary>
/// How long a message should be displayed for (in seconds).
/// </summary>
[Property, Group("Timing")] float MessageTime { get; set; } = 30f;
/// <summary>
/// How long a message should take to fade out (in seconds).
/// </summary>
[Property, Group("Timing")] float FadeTime { get; set; } = 0.5f;
/// <summary>
/// The position of the chat on the screen.
/// </summary>
[Property, Group("Styling")] Vector2 ChatPosition { get; set; } = new(5f, 95f);
/// <summary>
/// The size of the chat. Set to (0, 0) to scale automatically.
/// </summary>
[Property, Group("Styling")] Vector2 ChatSize { get; set; } = new(400f, 600f);
/// <summary>
/// The background color of the chat.
/// </summary>
[Property, Group("Styling")] Color BackgroundColor { get; set; } = Color.Transparent;
/// <summary>
/// The color of the text in the chat.
/// </summary>
[Property, Group("Styling")] Color TextColor { get; set; } = Color.White;
/// <summary>
/// The size of the text in the chat.
/// </summary>
[Property, Group("Styling")] float TextSize { get; set; } = 16f;
/// <summary>
/// The font family to use for the text in the chat.
/// </summary>
[Property, Group("Styling")] string FontFamily { get; set; } = "Poppins";
/// <summary>
/// The size of the gaps between messages in the chat.
/// </summary>
[Property, Group("Styling")] float GapSize { get; set; } = 8f;
/// <summary>
/// The padding around messages in the chat.
/// </summary>
[Property, Group("Styling")] float Padding { get; set; } = 8f;
/// <summary>
/// Whether or not to reverse the order of messages in the chat.
/// </summary>
[Property, Group("Styling")] bool ReverseOrder { get; set; } = false;
/// <summary>
/// Whether or not to use custom images for subscriber badges.
/// </summary>
[Property, Group("Badges")] bool HasCustomSubscriberBadges { get; set; } = false;
/// <summary>
/// The custom subscriber badges to use.
/// </summary>
[Property, Group("Badges"), InlineEditor, ShowIf(nameof(TwitchChatExample.HasCustomSubscriberBadges), true)] CustomSubscriberBadges SubscriberBadges { get; set; } = new();
TwitchChatConnection Twitch;
protected override void OnEnabled()
{
if (string.IsNullOrWhiteSpace(Username)) return;
Twitch = new TwitchChatConnection(Username);
Twitch.OnMessageReceived += OnMessageReceived;
Twitch.OnMessageRemoved += OnMessageRemoved;
Twitch.OnChatCleared += OnChatCleared;
}
protected override void OnDisabled()
{
if (Twitch is not null)
{
Twitch.OnMessageReceived -= OnMessageReceived;
Twitch.OnMessageRemoved -= OnMessageRemoved;
Twitch.OnChatCleared -= OnChatCleared;
Twitch.Dispose();
}
}
protected override void OnUpdate()
{
Panel.Style.BackgroundColor = BackgroundColor;
Panel.Style.FontColor = TextColor;
Panel.Style.FontSize = TextSize;
Panel.Style.FontFamily = FontFamily;
Panel.Style.RowGap = GapSize;
Panel.Style.Padding = Padding;
Panel.Style.FlexDirection = ReverseOrder ? FlexDirection.ColumnReverse : FlexDirection.Column;
Panel.Style.JustifyContent = Justify.FlexEnd;
if (ChatSize == 0)
{
Panel.Style.Width = Length.Auto;
Panel.Style.Height = Length.Auto;
}
else
{
Panel.Style.Width = Length.Pixels(ChatSize.x);
Panel.Style.Height = Length.Pixels(ChatSize.y);
}
Panel.Style.MaxWidth = Panel.Style.Width;
Panel.Style.MaxHeight = Panel.Style.Height;
if (ChatPosition.x < 50f)
{
Panel.Style.Left = Length.Percent(ChatPosition.x);
Panel.Style.Right = default;
}
else
{
Panel.Style.Left = default;
Panel.Style.Right = Length.Percent((100f - ChatPosition.x));
}
if (ChatPosition.y < 50f)
{
Panel.Style.Top = Length.Percent(ChatPosition.y);
Panel.Style.Bottom = default;
}
else
{
Panel.Style.Top = default;
Panel.Style.Bottom = Length.Percent((100f - ChatPosition.y));
}
}
void OnMessageReceived(TwitchChatMessage message)
{
var entry = Panel.AddChild<TwitchChatEntry>();
entry.ChatMessage = message;
entry.Lifetime = MessageTime;
entry.HasAvatarImages = HasAvatarImages;
}
void OnMessageRemoved(TwitchChatMessage message)
{
foreach (var child in Panel.Children)
{
if (child is TwitchChatEntry entry && entry.ChatMessage == message)
{
entry.Delete();
break;
}
}
}
void OnChatCleared()
{
Panel.DeleteChildren();
}
protected override int BuildHash() => System.HashCode.Combine(Username);
}@using Sandbox.UI;
@namespace Duccsoft
@inherits Panel
<root class="video-texture">
@if ( ShowControls )
{
<VideoControlOverlay VideoPanel=@this AutoHide=@AutoHideControls AutoHideDelay=@AutoHideDelay/>
}
</root>
@using Sandbox;
@using Sandbox.UI;
@inherits PanelComponent
@namespace TwitchAPI.Examples
<root>
</root>
@code
{
/// <summary>
/// The username of the Twitch Chat to connect to.
/// </summary>
[Property] string Username { get; set; }
/// <summary>
/// Whether or not to include the user's avatar images in the chat.
/// </summary>
[Property] bool HasAvatarImages { get; set; } = false;
/// <summary>
/// How long a message should be displayed for (in seconds).
/// </summary>
[Property, Group("Timing")] float MessageTime { get; set; } = 30f;
/// <summary>
/// How long a message should take to fade out (in seconds).
/// </summary>
[Property, Group("Timing")] float FadeTime { get; set; } = 0.5f;
/// <summary>
/// The position of the chat on the screen.
/// </summary>
[Property, Group("Styling")] Vector2 ChatPosition { get; set; } = new(5f, 95f);
/// <summary>
/// The size of the chat. Set to (0, 0) to scale automatically.
/// </summary>
[Property, Group("Styling")] Vector2 ChatSize { get; set; } = new(400f, 600f);
/// <summary>
/// The background color of the chat.
/// </summary>
[Property, Group("Styling")] Color BackgroundColor { get; set; } = Color.Transparent;
/// <summary>
/// The color of the text in the chat.
/// </summary>
[Property, Group("Styling")] Color TextColor { get; set; } = Color.White;
/// <summary>
/// The size of the text in the chat.
/// </summary>
[Property, Group("Styling")] float TextSize { get; set; } = 16f;
/// <summary>
/// The font family to use for the text in the chat.
/// </summary>
[Property, Group("Styling")] string FontFamily { get; set; } = "Poppins";
/// <summary>
/// The size of the gaps between messages in the chat.
/// </summary>
[Property, Group("Styling")] float GapSize { get; set; } = 8f;
/// <summary>
/// The padding around messages in the chat.
/// </summary>
[Property, Group("Styling")] float Padding { get; set; } = 8f;
/// <summary>
/// Whether or not to reverse the order of messages in the chat.
/// </summary>
[Property, Group("Styling")] bool ReverseOrder { get; set; } = false;
/// <summary>
/// Whether or not to use custom images for subscriber badges.
/// </summary>
[Property, Group("Badges")] bool HasCustomSubscriberBadges { get; set; } = false;
/// <summary>
/// The custom subscriber badges to use.
/// </summary>
[Property, Group("Badges"), InlineEditor, ShowIf(nameof(TwitchChatExample.HasCustomSubscriberBadges), true)] CustomSubscriberBadges SubscriberBadges { get; set; } = new();
TwitchChatConnection Twitch;
protected override void OnEnabled()
{
if (string.IsNullOrWhiteSpace(Username)) return;
Twitch = new TwitchChatConnection(Username);
Twitch.OnMessageReceived += OnMessageReceived;
Twitch.OnMessageRemoved += OnMessageRemoved;
Twitch.OnChatCleared += OnChatCleared;
}
protected override void OnDisabled()
{
if (Twitch is not null)
{
Twitch.OnMessageReceived -= OnMessageReceived;
Twitch.OnMessageRemoved -= OnMessageRemoved;
Twitch.OnChatCleared -= OnChatCleared;
Twitch.Dispose();
}
}
protected override void OnUpdate()
{
Panel.Style.BackgroundColor = BackgroundColor;
Panel.Style.FontColor = TextColor;
Panel.Style.FontSize = TextSize;
Panel.Style.FontFamily = FontFamily;
Panel.Style.RowGap = GapSize;
Panel.Style.Padding = Padding;
Panel.Style.FlexDirection = ReverseOrder ? FlexDirection.ColumnReverse : FlexDirection.Column;
Panel.Style.JustifyContent = Justify.FlexEnd;
if (ChatSize == 0)
{
Panel.Style.Width = Length.Auto;
Panel.Style.Height = Length.Auto;
}
else
{
Panel.Style.Width = Length.Pixels(ChatSize.x);
Panel.Style.Height = Length.Pixels(ChatSize.y);
}
Panel.Style.MaxWidth = Panel.Style.Width;
Panel.Style.MaxHeight = Panel.Style.Height;
if (ChatPosition.x < 50f)
{
Panel.Style.Left = Length.Percent(ChatPosition.x);
Panel.Style.Right = default;
}
else
{
Panel.Style.Left = default;
Panel.Style.Right = Length.Percent((100f - ChatPosition.x));
}
if (ChatPosition.y < 50f)
{
Panel.Style.Top = Length.Percent(ChatPosition.y);
Panel.Style.Bottom = default;
}
else
{
Panel.Style.Top = default;
Panel.Style.Bottom = Length.Percent((100f - ChatPosition.y));
}
}
void OnMessageReceived(TwitchChatMessage message)
{
var entry = Panel.AddChild<TwitchChatEntry>();
entry.ChatMessage = message;
entry.Lifetime = MessageTime;
entry.HasAvatarImages = HasAvatarImages;
}
void OnMessageRemoved(TwitchChatMessage message)
{
foreach (var child in Panel.Children)
{
if (child is TwitchChatEntry entry && entry.ChatMessage == message)
{
entry.Delete();
break;
}
}
}
void OnChatCleared()
{
Panel.DeleteChildren();
}
protected override int BuildHash() => System.HashCode.Combine(Username);
}