Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions AutomaticAds.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@

namespace AutomaticAds;

[MinimumApiVersion(315)]
[MinimumApiVersion(337)]
public class AutomaticAdsBase : BasePlugin, IPluginConfig<BaseConfigs>
{
public override string ModuleName => "AutomaticAds";
public override string ModuleVersion => "1.2.7";
public override string ModuleVersion => "1.2.8";
public override string ModuleAuthor => "luca.uy";
public override string ModuleDescription => "Send automatic messages to the chat and play a sound alert for users to see the message.";

Expand Down
2 changes: 1 addition & 1 deletion AutomaticAds.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.315" />
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.337" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<None Update="lang\**\*.*" CopyToOutputDirectory="PreserveNewest" />
<None Update="Newtonsoft.Json.dll" CopyToOutputDirectory="PreserveNewest" />
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# AutomaticAds CS2
This plugin allows you to schedule and display announcements in the chat at customizable intervals. Each announcement is accompanied by a brief sound effect to capture players' attention seamlessly.

> [!WARNING]
> After the update on 07/28/2025, ads with displayType “Screen” have stopped working. It is still too early to know if there is a solution or not.
> [!CAUTION]
> Ads with `"Screen"` have been re-enabled. However, the current solution does not work as well as it did before the 07/28/2025 update. It is recommended not to use this feature for now, pending a more stable method.

https://github.com/user-attachments/assets/aae16cc4-7c67-477a-8c89-437d5c035211

Expand Down
75 changes: 38 additions & 37 deletions Services/ScreenTextService.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.Utils;
using System.Drawing;

Expand All @@ -11,6 +10,7 @@ namespace AutomaticAds.Services;
public class ScreenTextService
{
private readonly Dictionary<CCSPlayerController, CPointWorldText> _playerTexts = new();
private readonly Dictionary<CCSPlayerController, CPointOrient> _playerAnchors = new();
private readonly TimerManager _timerManager;
private readonly float _displayTime;

Expand Down Expand Up @@ -41,10 +41,10 @@ public void ShowTextOnScreen(CCSPlayerController player, string text, float posi

HideTextFromScreen(player);

var viewModel = EnsureCustomViewModel(player);
if (viewModel == null)
var anchorEntity = EnsurePlayerAnchor(player);
if (anchorEntity == null)
{
Console.WriteLine($"[AutomaticAds] Error: Could not create ViewModel for {player.PlayerName}");
Console.WriteLine($"[AutomaticAds] Error: Could not create or get anchor entity for {player.PlayerName}");
return;
}

Expand All @@ -62,7 +62,7 @@ public void ShowTextOnScreen(CCSPlayerController player, string text, float posi
fontName: "Tahoma Bold",
position: vectorData.Value.Position,
angle: vectorData.Value.Angle,
viewModel: viewModel,
parentEntity: anchorEntity,
depthOffset: 0.0f
);

Expand Down Expand Up @@ -91,41 +91,55 @@ public void ClearAllPlayerTexts()
textEntity.Remove();
}
_playerTexts.Clear();

foreach (var anchorEntity in _playerAnchors.Values)
{
if (anchorEntity?.IsValid == true)
anchorEntity.Remove();
}
_playerAnchors.Clear();
}

public void OnPlayerDisconnect(CCSPlayerController player)
{
HideTextFromScreen(player);

if (_playerAnchors.TryGetValue(player, out var anchorEntity))
{
if (anchorEntity?.IsValid == true)
anchorEntity.Remove();
_playerAnchors.Remove(player);
}
}

private CCSGOViewModel? EnsureCustomViewModel(CCSPlayerController player)
private CPointOrient? EnsurePlayerAnchor(CCSPlayerController player)
{
if (_playerAnchors.TryGetValue(player, out var anchor) && anchor.IsValid)
{
return anchor;
}

var pawn = GetPlayerPawn(player);
if (pawn?.ViewModelServices == null) return null;
if (pawn == null) return null;

try
{
int offset = Schema.GetSchemaOffset("CCSPlayer_ViewModelServices", "m_hViewModel");
IntPtr viewModelHandleAddress = pawn.ViewModelServices.Handle + offset + 4;

var handle = new CHandle<CCSGOViewModel>(viewModelHandleAddress);

if (!handle.IsValid)
var newAnchor = Utilities.CreateEntityByName<CPointOrient>("point_orient");
if (newAnchor == null || !newAnchor.IsValid)
{
var viewModel = Utilities.CreateEntityByName<CCSGOViewModel>("predicted_viewmodel");
if (viewModel != null)
{
viewModel.DispatchSpawn();
handle.Raw = viewModel.EntityHandle.Raw;
Utilities.SetStateChanged(pawn, "CCSPlayerPawnBase", "m_pViewModelServices");
}
Console.WriteLine($"[AutomaticAds] Error: Could not create CPointOrient for {player.PlayerName}");
return null;
}

return handle.Value;
newAnchor.DispatchSpawn();
newAnchor.AcceptInput("SetParent", pawn, null, "!activator");

_playerAnchors[player] = newAnchor;
return newAnchor;
}
catch (Exception ex)
{
Console.WriteLine($"[AutomaticAds] Error creating ViewModel: {ex.Message}");
Console.WriteLine($"[AutomaticAds] Error creating anchor entity for {player.PlayerName}: {ex.Message}");
return null;
}
}
Expand All @@ -151,11 +165,6 @@ public void OnPlayerDisconnect(CCSPlayerController player)

public readonly record struct VectorData(Vector Position, QAngle Angle);

private VectorData? CalculateTextPosition(CCSPlayerController player)
{
return CalculateTextPosition(player, PositionX, PositionY);
}

private VectorData? CalculateTextPosition(CCSPlayerController player, float positionX, float positionY)
{
var playerPawn = GetPlayerPawn(player);
Expand Down Expand Up @@ -201,15 +210,7 @@ public void OnPlayerDisconnect(CCSPlayerController player)
return (x * scaleFactor, y * scaleFactor);
}

private CPointWorldText? CreateWorldTextEntity(
string text,
int fontSize,
Color color,
string fontName,
Vector position,
QAngle angle,
CCSGOViewModel viewModel,
float depthOffset = 0.1f)
private CPointWorldText? CreateWorldTextEntity(string text, int fontSize, Color color, string fontName, Vector position, QAngle angle, CPointOrient parentEntity, float depthOffset = 0.1f)
{
try
{
Expand Down Expand Up @@ -239,7 +240,7 @@ public void OnPlayerDisconnect(CCSPlayerController player)
entity.DispatchSpawn();
entity.Teleport(position, angle, null);

entity.AcceptInput("SetParent", viewModel, null, "!activator");
entity.AcceptInput("SetParent", parentEntity, null, "!activator");

return entity;
}
Expand Down