mirror of
https://github.com/edgegamers/Jailbreak.git
synced 2025-12-06 04:42:57 -08:00
Compare commits
157 Commits
4.6.2
...
400a5171a7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
400a5171a7 | ||
|
|
edd6dfdab6 | ||
|
|
8bdfff38e5 | ||
|
|
d5f8c80b0d | ||
|
|
6bc0395768 | ||
|
|
a4b5394e0b | ||
|
|
1434b42e79 | ||
|
|
6d01e2e58d | ||
|
|
4de90e6ae9 | ||
|
|
981025facd | ||
|
|
2c50333446 | ||
|
|
d2db34b509 | ||
|
|
53e75cdc22 | ||
|
|
0cb7f833dc | ||
|
|
5fdefd67ee | ||
|
|
04c383916c | ||
|
|
b78b05c73f | ||
|
|
4f121106f9 | ||
|
|
d57cefaad2 | ||
|
|
d91cede2a8 | ||
|
|
588b9748c4 | ||
|
|
8ba25a410e | ||
|
|
7a60ff7c6d | ||
|
|
74e20acfe3 | ||
|
|
900e79401f | ||
|
|
a40a0841ea | ||
|
|
b165823621 | ||
|
|
19215e2695 | ||
|
|
c5d050c226 | ||
|
|
5957833e81 | ||
|
|
cecf3cec04 | ||
|
|
94db4d7252 | ||
|
|
af623b2ea5 | ||
|
|
b4aeb9b8ee | ||
|
|
5213241dfc | ||
|
|
ec45ba581f | ||
|
|
a832e43230 | ||
|
|
3ec269eef0 | ||
|
|
5a6e1a56c3 | ||
|
|
8f556329cb | ||
|
|
ee12f527eb | ||
|
|
5d42dacf43 | ||
|
|
e7e799abb3 | ||
|
|
b2f2411b1b | ||
|
|
6844272ba2 | ||
|
|
144f879562 | ||
|
|
0d0453cfe6 | ||
|
|
66d5d245de | ||
|
|
df599050f2 | ||
|
|
96bc068494 | ||
|
|
39c6ad6270 | ||
|
|
8f942383c6 | ||
|
|
7dd822b6de | ||
|
|
33c4389fcf | ||
|
|
587d64fcf5 | ||
|
|
4b412dcc7b | ||
|
|
b5603fafbc | ||
|
|
4df6446b18 | ||
|
|
8851e27004 | ||
|
|
ece5e9bb35 | ||
|
|
19f240d918 | ||
|
|
d7f1149317 | ||
|
|
bda2fd7a43 | ||
|
|
fc8b7f0cff | ||
|
|
75094be9cf | ||
|
|
22e9e45811 | ||
|
|
6a804dd2a5 | ||
|
|
9e69f774ce | ||
|
|
963bb85c28 | ||
|
|
cccf2c41e7 | ||
|
|
a4a50e6a6d | ||
|
|
31ada4a226 | ||
|
|
310606f37f | ||
|
|
f0b96663bc | ||
|
|
5a98918f71 | ||
|
|
8f370df230 | ||
|
|
97381652ec | ||
|
|
f010bce92c | ||
|
|
d8996efb41 | ||
|
|
11b527d9fc | ||
|
|
888906fc3a | ||
|
|
c9083a3630 | ||
|
|
e75baccdd0 | ||
|
|
ef74889949 | ||
|
|
ce27b3ad45 | ||
|
|
169f9883ae | ||
|
|
5ce074f0b1 | ||
|
|
274319e95f | ||
|
|
dc49770716 | ||
|
|
5972950f78 | ||
|
|
aad9263123 | ||
|
|
43926f8ec4 | ||
|
|
4fb8e5c599 | ||
|
|
74505b647d | ||
|
|
9cf357a522 | ||
|
|
3d9283993e | ||
|
|
bec433f05c | ||
|
|
6961fa9c81 | ||
|
|
537744712d | ||
|
|
37f6cccecf | ||
|
|
34fcf653c8 | ||
|
|
da31614dec | ||
|
|
a24963cb17 | ||
|
|
d2e048f3d8 | ||
|
|
51f87a9c08 | ||
|
|
9067a09b03 | ||
|
|
921613ab65 | ||
|
|
7ec893ddda | ||
|
|
3406fff706 | ||
|
|
d2c945bf43 | ||
|
|
7e5e4bca29 | ||
|
|
fd43b750a7 | ||
|
|
064f0eb51a | ||
|
|
8e8a22194e | ||
|
|
2a062b0a7e | ||
|
|
3f1d50d584 | ||
|
|
9ce8893aa0 | ||
|
|
c37c2d3b0d | ||
|
|
2952e98bfa | ||
|
|
c9fc1f444c | ||
|
|
3d677fb679 | ||
|
|
0e591ebe79 | ||
|
|
12b58160c1 | ||
|
|
7650556948 | ||
|
|
6cf980bd14 | ||
|
|
e06b8c4623 | ||
|
|
ec6606f800 | ||
|
|
f072f392fe | ||
|
|
9bd1668e06 | ||
|
|
c58e9a033e | ||
|
|
b34d984ffb | ||
|
|
12f8fa7819 | ||
|
|
3510375c4a | ||
|
|
7688fa67d9 | ||
|
|
cc7b21286a | ||
|
|
7b8a6c514b | ||
|
|
f9be61e62d | ||
|
|
9ef36aa97e | ||
|
|
4482291c44 | ||
|
|
4413de9933 | ||
|
|
fe42cd41dd | ||
|
|
2744ca54c0 | ||
|
|
0936f57f2a | ||
|
|
c155e3e720 | ||
|
|
c70cad0614 | ||
|
|
f029049302 | ||
|
|
1938ba65ab | ||
|
|
80f1cf8802 | ||
|
|
3aad5eba3c | ||
|
|
2b47b10afb | ||
|
|
95bd6a0ff2 | ||
|
|
17acc5f174 | ||
|
|
1cddf43798 | ||
|
|
00ab901022 | ||
|
|
4bf9924ddb | ||
|
|
294d761cf8 | ||
|
|
cc206db226 |
6
.github/workflows/nightly.yml
vendored
6
.github/workflows/nightly.yml
vendored
@@ -3,7 +3,11 @@
|
||||
|
||||
name: Nightlies
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
schedule:
|
||||
- cron: '15 0 * * 3' # Every Wednesday at 00:15 UTC
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
BIN
.gitignore
vendored
BIN
.gitignore
vendored
Binary file not shown.
26
lang/Jailbreak.English/SpecialDay/FogDayLocale.cs
Normal file
26
lang/Jailbreak.English/SpecialDay/FogDayLocale.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using Jailbreak.Formatting.Base;
|
||||
|
||||
namespace Jailbreak.English.SpecialDay;
|
||||
|
||||
public class FogDayLocale() : SoloDayLocale("Fog War",
|
||||
"A heavy fog is creeping in...", "Your visibility will be gone soon.",
|
||||
"Fog expands periodically — Stay alert.") {
|
||||
|
||||
public IView FogComingIn() {
|
||||
return new SimpleView {
|
||||
PREFIX,
|
||||
"Fog's rolling in!",
|
||||
"Match Starts in 15 seconds."
|
||||
};
|
||||
}
|
||||
|
||||
public IView FogExpandsIn(int seconds) {
|
||||
if (seconds == 0) return new SimpleView { PREFIX, "Fog is expanding." };
|
||||
return new SimpleView {
|
||||
PREFIX,
|
||||
"Fog will start expanding in",
|
||||
seconds,
|
||||
"second" + (seconds == 1 ? "" : "s")
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -78,6 +78,11 @@ public class WardenLocale : IWardenLocale,
|
||||
=> new SimpleView {
|
||||
PREFIX, "The fire command failed for some unknown reason..."
|
||||
};
|
||||
|
||||
public IView TogglingNotEnabled
|
||||
=> new SimpleView {
|
||||
PREFIX, "Toggling Auto-Warden is not supported on this server."
|
||||
};
|
||||
|
||||
public IView PassWarden(CCSPlayerController player) {
|
||||
return new SimpleView {
|
||||
@@ -133,4 +138,13 @@ public class WardenLocale : IWardenLocale,
|
||||
PREFIX, $"{marker}{ChatColors.Grey} marker removed."
|
||||
};
|
||||
}
|
||||
|
||||
public IView AutoWardenToggled(bool enabled) {
|
||||
return new SimpleView {
|
||||
PREFIX,
|
||||
ChatColors.Grey + "You",
|
||||
enabled ? ChatColors.Green + "enabled" : ChatColors.Red + "disabled",
|
||||
ChatColors.Grey + "Auto-Warden."
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ using Microsoft.Extensions.Localization;
|
||||
using MStatsShared;
|
||||
|
||||
namespace Jailbreak.LastRequest;
|
||||
//TODO: Fix Various Server Crashes
|
||||
|
||||
public class LastRequestManager(ILRLocale messages, IServiceProvider provider)
|
||||
: ILastRequestManager, IDamageBlocker {
|
||||
@@ -64,30 +65,31 @@ public class LastRequestManager(ILRLocale messages, IServiceProvider provider)
|
||||
private ILastRequestFactory? factory;
|
||||
public bool IsLREnabledForRound { get; set; } = true;
|
||||
|
||||
public bool ShouldBlockDamage(CCSPlayerController player,
|
||||
public bool ShouldBlockDamage(CCSPlayerController victim,
|
||||
CCSPlayerController? attacker) {
|
||||
if (!IsLREnabled) return false;
|
||||
|
||||
if (attacker == null || !attacker.IsReal()) return false;
|
||||
|
||||
var playerLR = ((ILastRequestManager)this).GetActiveLR(player);
|
||||
var victimLR = ((ILastRequestManager)this).GetActiveLR(victim);
|
||||
var attackerLR = ((ILastRequestManager)this).GetActiveLR(attacker);
|
||||
|
||||
if (playerLR == null && attackerLR == null)
|
||||
if (victimLR == null && attackerLR == null)
|
||||
// Neither of them is in an LR
|
||||
return false;
|
||||
|
||||
if (playerLR == null != (attackerLR == null)) {
|
||||
if (victimLR == null != (attackerLR == null)) {
|
||||
// One of them is in an LR
|
||||
messages.DamageBlockedInsideLastRequest.ToCenter(attacker);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Both of them are in LR, verify they're in same LR
|
||||
if (playerLR == null) return false;
|
||||
if (victimLR == null) return false;
|
||||
|
||||
if (playerLR.Prisoner.Equals(attacker) || playerLR.Guard.Equals(attacker))
|
||||
// Same LR, allow damage
|
||||
if (victimLR.Prisoner.SteamID == attacker.SteamID
|
||||
|| victimLR.Guard.SteamID == attacker.SteamID)
|
||||
// The person attacking is the victim's LR participant, allow damage
|
||||
return false;
|
||||
|
||||
messages.DamageBlockedNotInSameLR.ToCenter(attacker);
|
||||
|
||||
@@ -64,6 +64,15 @@ public class LastRequestRebelCommand(ILastRequestManager lastRequestManager,
|
||||
return;
|
||||
}
|
||||
|
||||
if (Utilities.GetPlayers()
|
||||
.Count(p
|
||||
=> p.IsReal() && p is { PawnIsAlive: true, Team: CsTeam.Terrorist })
|
||||
> 1) {
|
||||
messages.CannotLR("You must be the last alive to !rebel")
|
||||
.ToChat(rebeller);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rebeller.Pawn.Value != null)
|
||||
rebellerHealths[rebeller.Slot] = rebeller.Pawn.Value.Health;
|
||||
lastRequestRebelManager.StartLRRebelling(rebeller);
|
||||
|
||||
@@ -95,8 +95,7 @@ public class BulletForBullet(BasePlugin plugin, IServiceProvider provider,
|
||||
}, TimerFlags.STOP_ON_MAPCHANGE);
|
||||
}
|
||||
|
||||
private HookResult OnWeaponFire(EventWeaponFire @event,
|
||||
GameEventInfo info) {
|
||||
private HookResult OnWeaponFire(EventWeaponFire @event, GameEventInfo info) {
|
||||
if (State != LRState.ACTIVE) return HookResult.Continue;
|
||||
|
||||
var player = @event.Userid;
|
||||
@@ -124,6 +123,15 @@ public class BulletForBullet(BasePlugin plugin, IServiceProvider provider,
|
||||
public override void OnEnd(LRResult result) {
|
||||
Plugin.DeregisterEventHandler<EventWeaponFire>(OnWeaponFire);
|
||||
State = LRState.COMPLETED;
|
||||
|
||||
switch (result) {
|
||||
case LRResult.GUARD_WIN when Guard.IsValid:
|
||||
Guard.GiveNamedItem("weapon_knife");
|
||||
break;
|
||||
case LRResult.PRISONER_WIN when Prisoner.IsValid:
|
||||
Prisoner.GiveNamedItem("weapon_knife");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool PreventEquip(CCSPlayerController player,
|
||||
|
||||
@@ -56,6 +56,13 @@ public class GunToss(BasePlugin plugin, ILastRequestManager manager,
|
||||
}
|
||||
|
||||
if (prisonerTossed && guardTossed) bothThrewTick = Server.TickCount;
|
||||
|
||||
if (bothThrewTick > 0)
|
||||
Plugin.AddTimer(5, () => {
|
||||
if (State != LRState.ACTIVE) return;
|
||||
Guard.SetHealth(Math.Min(Guard.PlayerPawn.Value!.Health, 100));
|
||||
Guard.SetArmor(Math.Min(Guard.PawnArmor, 100));
|
||||
});
|
||||
}
|
||||
|
||||
public override void Setup() {
|
||||
@@ -64,6 +71,13 @@ public class GunToss(BasePlugin plugin, ILastRequestManager manager,
|
||||
Prisoner.RemoveWeapons();
|
||||
Guard.RemoveWeapons();
|
||||
|
||||
Server.NextFrame(() => {
|
||||
if (!Guard.IsValid) return;
|
||||
|
||||
Guard.SetHealth(500);
|
||||
Guard.SetArmor(500);
|
||||
});
|
||||
|
||||
Plugin.AddTimer(3, Execute);
|
||||
}
|
||||
|
||||
|
||||
@@ -94,6 +94,15 @@ public class MuteSystem(IServiceProvider provider)
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
[GameEventHandler]
|
||||
public HookResult OnDeath(EventPlayerDeath @event, GameEventInfo info) {
|
||||
var player = @event.Userid;
|
||||
if (player == null || !player.IsReal()) return HookResult.Continue;
|
||||
|
||||
mute(player);
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
private void unmuteGuards() {
|
||||
foreach (var player in Utilities.GetPlayers()
|
||||
.Where(player => player is {
|
||||
|
||||
@@ -17,6 +17,7 @@ using Jailbreak.Public.Behaviors;
|
||||
using Jailbreak.Public.Extensions;
|
||||
using Jailbreak.Public.Mod.LastRequest;
|
||||
using Jailbreak.Public.Mod.Rebel;
|
||||
using Jailbreak.Public.Utils;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using MStatsShared;
|
||||
using Timer = CounterStrikeSharp.API.Modules.Timers.Timer;
|
||||
@@ -46,12 +47,6 @@ public class C4Behavior(IC4Locale ic4Locale, IRebelService rebelService,
|
||||
|
||||
private int roundStart = 0;
|
||||
|
||||
// EmitSound(CBaseEntity* pEnt, const char* sSoundName, int nPitch, float flVolume, float flDelay)
|
||||
private readonly MemoryFunctionVoid<CBaseEntity, string, int, float, float>
|
||||
// ReSharper disable once InconsistentNaming
|
||||
CBaseEntity_EmitSoundParamsLinux = new(
|
||||
"48 B8 ? ? ? ? ? ? ? ? 55 48 89 E5 41 55 41 54 49 89 FC 53 48 89 F3"); // LINUX ONLY.
|
||||
|
||||
private readonly Dictionary<int, int> deathToKiller = new();
|
||||
private bool giveNextRound = true;
|
||||
|
||||
@@ -78,14 +73,13 @@ public class C4Behavior(IC4Locale ic4Locale, IRebelService rebelService,
|
||||
API.Stats?.PushStat(new ServerStat("JB_BOMB_ATTEMPT",
|
||||
$"{pos.X:F2} {pos.Y:F2} {pos.Z:F2}"));
|
||||
|
||||
tryEmitSound(player, "jb.jihad", 1, 1f, 0f);
|
||||
|
||||
bombs[bombEntity].IsDetonating = true;
|
||||
|
||||
rebelService.MarkRebel(player);
|
||||
|
||||
Server.RunOnTick(Server.TickCount + (int)(64 * delay),
|
||||
() => detonate(player, bombEntity));
|
||||
player.EmitSound("jb.jihad");
|
||||
}
|
||||
|
||||
public void TryGiveC4ToRandomTerrorist() {
|
||||
@@ -111,18 +105,21 @@ public class C4Behavior(IC4Locale ic4Locale, IRebelService rebelService,
|
||||
|
||||
public void Start(BasePlugin basePlugin) {
|
||||
plugin = basePlugin;
|
||||
plugin.RegisterListener<Listeners.OnTick>(playerUseC4ListenerCallback);
|
||||
plugin.RegisterListener<Listeners.OnPlayerButtonsChanged>(
|
||||
playerButtonsChanged);
|
||||
}
|
||||
|
||||
private void playerUseC4ListenerCallback() {
|
||||
private void playerButtonsChanged(CCSPlayerController player,
|
||||
PlayerButtons pressed, PlayerButtons released) {
|
||||
if ((pressed & PlayerButtons.Use) == 0) return;
|
||||
|
||||
foreach (var (bomb, meta) in bombs) {
|
||||
if (!bomb.IsValid) continue;
|
||||
if (meta.IsDetonating) continue;
|
||||
if (!bomb.IsValid || meta.IsDetonating) continue;
|
||||
|
||||
var bombCarrier = bomb.OwnerEntity.Value?.As<CCSPlayerPawn>()
|
||||
.Controller.Value?.As<CCSPlayerController>();
|
||||
if (bombCarrier == null || !bombCarrier.IsValid
|
||||
|| (bombCarrier.Buttons & PlayerButtons.Use) == 0)
|
||||
|| bombCarrier.Slot != player.Slot)
|
||||
continue;
|
||||
|
||||
var activeWeapon = bombCarrier.PlayerPawn.Value?.WeaponServices
|
||||
@@ -239,17 +236,6 @@ public class C4Behavior(IC4Locale ic4Locale, IRebelService rebelService,
|
||||
|
||||
if (Server.TickCount - roundStart < CV_C4_DELAY.Value * 64) return;
|
||||
|
||||
tryEmitSound(player, "jb.jihadExplosion", 1, 1f, 0f);
|
||||
var particleSystemEntity =
|
||||
Utilities.CreateEntityByName<CParticleSystem>("info_particle_system")!;
|
||||
particleSystemEntity.EffectName =
|
||||
"particles/explosions_fx/explosion_c4_500.vpcf";
|
||||
particleSystemEntity.StartActive = true;
|
||||
|
||||
particleSystemEntity.Teleport(player.PlayerPawn.Value!.AbsOrigin!,
|
||||
new QAngle(), new Vector());
|
||||
particleSystemEntity.DispatchSpawn();
|
||||
|
||||
var killed = 0;
|
||||
/* Calculate damage here, only applies to alive CTs. */
|
||||
var lrs = provider.GetRequiredService<ILastRequestManager>();
|
||||
@@ -280,6 +266,10 @@ public class C4Behavior(IC4Locale ic4Locale, IRebelService rebelService,
|
||||
}
|
||||
}
|
||||
|
||||
// If they didn't have the C4 make sure to remove it.
|
||||
player.CommitSuicide(true, true);
|
||||
bombs.Remove(bomb);
|
||||
|
||||
if (API.Gangs != null && killed > 0) {
|
||||
var eco = API.Gangs.Services.GetService<IEcoManager>();
|
||||
if (eco != null) {
|
||||
@@ -289,17 +279,18 @@ public class C4Behavior(IC4Locale ic4Locale, IRebelService rebelService,
|
||||
}
|
||||
}
|
||||
|
||||
player.EmitSound("jb.jihadExplosion");
|
||||
var particleSystemEntity =
|
||||
Utilities.CreateEntityByName<CParticleSystem>("info_particle_system")!;
|
||||
particleSystemEntity.EffectName =
|
||||
"particles/explosions_fx/explosion_c4_500.vpcf";
|
||||
particleSystemEntity.StartActive = true;
|
||||
|
||||
particleSystemEntity.Teleport(player.PlayerPawn.Value!.AbsOrigin!,
|
||||
new QAngle(), new Vector());
|
||||
particleSystemEntity.DispatchSpawn();
|
||||
|
||||
API.Stats?.PushStat(new ServerStat("JB_BOMB_EXPLODED", killed.ToString()));
|
||||
|
||||
// If they didn't have the C4 make sure to remove it.
|
||||
player.CommitSuicide(true, true);
|
||||
bombs.Remove(bomb);
|
||||
}
|
||||
|
||||
private void tryEmitSound(CBaseEntity entity, string soundEventName,
|
||||
int pitch, float volume, float delay) {
|
||||
CBaseEntity_EmitSoundParamsLinux.Invoke(entity, soundEventName, pitch,
|
||||
volume, delay);
|
||||
}
|
||||
|
||||
private class C4Metadata(bool isDetonating) {
|
||||
|
||||
@@ -67,6 +67,9 @@ public class RebelListener(IRebelService rebelService,
|
||||
var attacker = ev.Attacker;
|
||||
if (attacker == null || !attacker.IsValid || attacker.IsBot)
|
||||
return HookResult.Continue;
|
||||
|
||||
// Do not give player credits if they suicided
|
||||
if (player.SteamID == attacker.SteamID) return HookResult.Continue;
|
||||
|
||||
var eco = API.Gangs?.Services.GetService<IEcoManager>();
|
||||
if (eco == null) return HookResult.Continue;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -12,18 +12,22 @@ public class SpecialDayFactory(IServiceProvider provider) : ISpecialDayFactory {
|
||||
|
||||
public AbstractSpecialDay CreateSpecialDay(SDType type) {
|
||||
return type switch {
|
||||
SDType.BHOP => new BHopDay(plugin, provider),
|
||||
SDType.CUSTOM => new CustomDay(plugin, provider),
|
||||
SDType.FFA => new FFADay(plugin, provider),
|
||||
SDType.GUNGAME => new GunGameDay(plugin, provider),
|
||||
SDType.HNS => new HideAndSeekDay(plugin, provider),
|
||||
// SDType.INFECTION => new InfectionDay(plugin, provider),
|
||||
SDType.NOSCOPE => new NoScopeDay(plugin, provider),
|
||||
SDType.OITC => new OneInTheChamberDay(plugin, provider),
|
||||
SDType.SPEEDRUN => new SpeedrunDay(plugin, provider),
|
||||
SDType.TELEPORT => new TeleportDay(plugin, provider),
|
||||
SDType.WARDAY => new WardayDay(plugin, provider),
|
||||
_ => throw new NotImplementedException()
|
||||
SDType.BHOP => new BHopDay(plugin, provider),
|
||||
SDType.CUSTOM => new CustomDay(plugin, provider),
|
||||
SDType.FFA => new FFADay(plugin, provider),
|
||||
SDType.FOG => new FogDay(plugin, provider),
|
||||
SDType.GUNGAME => new GunGameDay(plugin, provider),
|
||||
SDType.GHOST => new GhostDay(plugin, provider),
|
||||
SDType.HE => new HEDay(plugin, provider),
|
||||
SDType.HNS => new HideAndSeekDay(plugin, provider),
|
||||
SDType.INFECTION => new InfectionDay(plugin, provider),
|
||||
SDType.NOSCOPE => new NoScopeDay(plugin, provider),
|
||||
SDType.OITC => new OneInTheChamberDay(plugin, provider),
|
||||
//SDType.ROCKETJUMP => new RocketJumpDay(plugin, provider),
|
||||
SDType.SPEEDRUN => new SpeedrunDay(plugin, provider),
|
||||
SDType.TELEPORT => new TeleportDay(plugin, provider),
|
||||
SDType.WARDAY => new WardayDay(plugin, provider),
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
206
mod/Jailbreak.SpecialDay/SpecialDays/FogDay.cs
Normal file
206
mod/Jailbreak.SpecialDay/SpecialDays/FogDay.cs
Normal file
@@ -0,0 +1,206 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using Jailbreak.English.SpecialDay;
|
||||
using Jailbreak.Formatting.Extensions;
|
||||
using Jailbreak.Formatting.Views.SpecialDay;
|
||||
using Jailbreak.Public.Mod.SpecialDay;
|
||||
using Jailbreak.Public.Mod.SpecialDay.Enums;
|
||||
|
||||
namespace Jailbreak.SpecialDay.SpecialDays;
|
||||
|
||||
public class FogDay(BasePlugin plugin, IServiceProvider provider)
|
||||
: AbstractSpecialDay(plugin, provider), ISpecialDayMessageProvider {
|
||||
public override SDType Type => SDType.FOG;
|
||||
private FogDayLocale Msg => (FogDayLocale)Locale;
|
||||
public ISDInstanceLocale Locale => new FogDayLocale();
|
||||
public override SpecialDaySettings Settings => new FogSettings();
|
||||
|
||||
private CFogController? fogController;
|
||||
private nint originalSkyMat;
|
||||
private nint originalSkyMatLighting;
|
||||
private CEnvSky? skyEntity;
|
||||
|
||||
private float targetFogEnd = 2000f;
|
||||
private float fogStepPerTick;
|
||||
private bool shouldGrowFog;
|
||||
|
||||
public override void Setup() {
|
||||
Plugin.RegisterListener<Listeners.OnTick>(onTick);
|
||||
Plugin.RegisterEventHandler<EventPlayerDeath>(onDeath);
|
||||
|
||||
cacheOriginalSky();
|
||||
|
||||
setFog(true);
|
||||
setSky(true);
|
||||
|
||||
Timers[20] += () => Locale.BeginsIn(20).ToAllChat();
|
||||
Timers[25] += () => {
|
||||
Msg.FogComingIn().ToAllChat();
|
||||
setTargetFogDistance(300, 10);
|
||||
};
|
||||
Timers[30] += () => Msg.BeginsIn(10).ToAllChat();
|
||||
Timers[35] += () => Msg.BeginsIn(5).ToAllChat();
|
||||
Timers[40] += () => {
|
||||
Execute();
|
||||
Msg.BeginsIn(0).ToAllChat();
|
||||
};
|
||||
Timers[97] += () => Msg.FogExpandsIn(3).ToAllChat();
|
||||
Timers[98] += () => Msg.FogExpandsIn(2).ToAllChat();
|
||||
Timers[99] += () => Msg.FogExpandsIn(1).ToAllChat();
|
||||
Timers[100] += () => {
|
||||
setTargetFogDistance(3000, 180);
|
||||
Msg.FogExpandsIn(0).ToAllChat();
|
||||
};
|
||||
|
||||
base.Setup();
|
||||
}
|
||||
|
||||
|
||||
private unsafe void setSky(bool set) {
|
||||
if (skyEntity == null) return;
|
||||
|
||||
var mat = set ? nint.Zero : originalSkyMat;
|
||||
var matLit = set ? nint.Zero : originalSkyMatLighting;
|
||||
|
||||
Unsafe.Write((void*)skyEntity.SkyMaterial.Handle, mat);
|
||||
Unsafe.Write((void*)skyEntity.SkyMaterialLightingOnly.Handle, matLit);
|
||||
|
||||
Utilities.SetStateChanged(skyEntity, "CEnvSky", "m_hSkyMaterial");
|
||||
Utilities.SetStateChanged(skyEntity, "CEnvSky",
|
||||
"m_hSkyMaterialLightingOnly");
|
||||
|
||||
skyEntity.BrightnessScale = 1.0f;
|
||||
Utilities.SetStateChanged(skyEntity, "CEnvSky", "m_flBrightnessScale");
|
||||
}
|
||||
|
||||
private void setFog(bool set) {
|
||||
if (!set) {
|
||||
fogController?.Remove();
|
||||
fogController = null;
|
||||
return;
|
||||
}
|
||||
|
||||
fogController ??=
|
||||
Utilities.CreateEntityByName<CFogController>("env_fog_controller");
|
||||
fogController?.DispatchSpawn();
|
||||
|
||||
if (fogController == null) return;
|
||||
|
||||
var fog = fogController.Fog;
|
||||
fog.Enable = true;
|
||||
fog.Blend = true;
|
||||
fog.ColorPrimary = Color.Black;
|
||||
fog.Exponent = 0.1f;
|
||||
fog.Maxdensity = 1f;
|
||||
fog.Start = 0;
|
||||
fog.End = targetFogEnd;
|
||||
fog.Farz = targetFogEnd * 1.04f;
|
||||
|
||||
foreach (var field in new[] {
|
||||
"colorPrimary", "start", "end", "farz", "maxdensity", "exponent",
|
||||
"enable", "blend",
|
||||
}) {
|
||||
Utilities.SetStateChanged(fogController, "CFogController", "m_fog",
|
||||
Schema.GetSchemaOffset("fogparams_t", field));
|
||||
}
|
||||
|
||||
var vis = Utilities
|
||||
.FindAllEntitiesByDesignerName<CPlayerVisibility>("env_player_visibility")
|
||||
.FirstOrDefault();
|
||||
if (vis != null) {
|
||||
vis.FogMaxDensityMultiplier = 1f;
|
||||
Utilities.SetStateChanged(vis, "CPlayerVisibility",
|
||||
"m_flFogMaxDensityMultiplier");
|
||||
}
|
||||
|
||||
foreach (var pawn in Utilities.GetPlayers()
|
||||
.Select(p => p.Pawn.Value)
|
||||
.OfType<CBasePlayerPawn>())
|
||||
pawn.AcceptInput("SetFogController", activator: fogController,
|
||||
value: "!activator");
|
||||
}
|
||||
|
||||
private void onTick() {
|
||||
if (!shouldGrowFog || fogController == null) return;
|
||||
|
||||
var fog = fogController.Fog;
|
||||
|
||||
if (Math.Abs(fog.End - targetFogEnd) < Math.Abs(fogStepPerTick)) {
|
||||
fog.End = targetFogEnd;
|
||||
fog.Farz = targetFogEnd * 1.04f;
|
||||
shouldGrowFog = false;
|
||||
} else {
|
||||
fog.End += fogStepPerTick;
|
||||
fog.Farz = fog.End + 10f;
|
||||
}
|
||||
|
||||
Utilities.SetStateChanged(fogController, "CFogController", "m_fog",
|
||||
Schema.GetSchemaOffset("fogparams_t", "end"));
|
||||
Utilities.SetStateChanged(fogController, "CFogController", "m_fog",
|
||||
Schema.GetSchemaOffset("fogparams_t", "farz"));
|
||||
}
|
||||
|
||||
private HookResult onDeath(EventPlayerDeath @event, GameEventInfo info) {
|
||||
var pawn = @event.Userid?.PlayerPawn.Value;
|
||||
if (pawn == null) return HookResult.Continue;
|
||||
|
||||
Server.NextFrame(() => {
|
||||
pawn.AcceptInput("SetFogController", activator: fogController,
|
||||
value: "!activator");
|
||||
});
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
override protected HookResult OnEnd(EventRoundEnd @event,
|
||||
GameEventInfo info) {
|
||||
setSky(false);
|
||||
setFog(false);
|
||||
|
||||
if (fogController != null) fogController.Remove();
|
||||
|
||||
Plugin.RemoveListener<Listeners.OnTick>(onTick);
|
||||
Plugin.DeregisterEventHandler<EventPlayerDeath>(onDeath);
|
||||
|
||||
return base.OnEnd(@event, info);
|
||||
}
|
||||
|
||||
private unsafe void cacheOriginalSky() {
|
||||
skyEntity = Utilities.FindAllEntitiesByDesignerName<CEnvSky>("env_sky")
|
||||
.FirstOrDefault();
|
||||
if (skyEntity == null) return;
|
||||
|
||||
originalSkyMat = Unsafe.Read<nint>((void*)skyEntity.SkyMaterial.Handle);
|
||||
originalSkyMatLighting =
|
||||
Unsafe.Read<nint>((void*)skyEntity.SkyMaterialLightingOnly.Handle);
|
||||
}
|
||||
|
||||
private void setTargetFogDistance(int distance, float durationInSeconds) {
|
||||
if (fogController == null) return;
|
||||
|
||||
var currentEnd = fogController.Fog.End;
|
||||
var totalTicks = durationInSeconds * 64f;
|
||||
fogStepPerTick = (distance - currentEnd) / totalTicks;
|
||||
targetFogEnd = distance;
|
||||
shouldGrowFog = true;
|
||||
}
|
||||
|
||||
public class FogSettings : SpecialDaySettings {
|
||||
private readonly Random rng;
|
||||
|
||||
public FogSettings() {
|
||||
TTeleport = TeleportType.ARMORY;
|
||||
CtTeleport = TeleportType.ARMORY;
|
||||
OpenCells = true;
|
||||
StripToKnife = false;
|
||||
rng = new Random();
|
||||
WithFriendlyFire();
|
||||
}
|
||||
|
||||
public override float FreezeTime(CCSPlayerController player) {
|
||||
return rng.NextSingle() * 5 + 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
153
mod/Jailbreak.SpecialDay/SpecialDays/GhostDay.cs
Normal file
153
mod/Jailbreak.SpecialDay/SpecialDays/GhostDay.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||
using CounterStrikeSharp.API.Modules.Timers;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using Jailbreak.English.SpecialDay;
|
||||
using Jailbreak.Formatting.Views.SpecialDay;
|
||||
using Jailbreak.Public.Extensions;
|
||||
using Jailbreak.Public.Mod.SpecialDay;
|
||||
using Jailbreak.Public.Mod.SpecialDay.Enums;
|
||||
using Jailbreak.Public.Utils;
|
||||
using Jailbreak.Validator;
|
||||
using Timer = CounterStrikeSharp.API.Modules.Timers.Timer;
|
||||
|
||||
namespace Jailbreak.SpecialDay.SpecialDays;
|
||||
|
||||
public class GhostDay(BasePlugin plugin, IServiceProvider provider)
|
||||
: FFADay(plugin, provider), ISpecialDayMessageProvider {
|
||||
public static readonly FakeConVar<float> CV_VISIBLE_DURATION = new(
|
||||
"css_jb_sd_ghost_visible_duration",
|
||||
"Amount of time players spend visible per cycle", 5f,
|
||||
ConVarFlags.FCVAR_NONE, new NonZeroRangeValidator<float>(1f, 30f));
|
||||
|
||||
public static readonly FakeConVar<float> CV_INVISIBLE_DURATION = new(
|
||||
"css_jb_sd_ghost_invisible_duration",
|
||||
"Amount of time players spend invisible per cycle", 5f,
|
||||
ConVarFlags.FCVAR_NONE, new NonZeroRangeValidator<float>(1f, 30f));
|
||||
|
||||
private static readonly
|
||||
MemoryFunctionVoid<nint, nint, int, nint, nint, nint, int, bool>
|
||||
CHECK_TRANSMIT = new(GameData.GetSignature("CheckTransmit"));
|
||||
|
||||
private static readonly int CHECK_TRANSMIT_PLAYER_SLOT_CACHE =
|
||||
GameData.GetOffset("CheckTransmitPlayerSlot");
|
||||
|
||||
private static float CycleDuration
|
||||
=> CV_VISIBLE_DURATION.Value + CV_INVISIBLE_DURATION.Value;
|
||||
private bool allPlayersVisible;
|
||||
private float timeElapsed;
|
||||
private Timer? ghostTimer;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct CCheckTransmitInfo
|
||||
{
|
||||
public CFixedBitVecBase m_pTransmitEntity;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public readonly unsafe struct CFixedBitVecBase
|
||||
{
|
||||
private const int LOG2_BITS_PER_INT = 5;
|
||||
private const int MAX_EDICT_BITS = 14;
|
||||
private const int BITS_PER_INT = 32;
|
||||
private const int MAX_EDICTS = 1 << MAX_EDICT_BITS;
|
||||
|
||||
private readonly uint* m_Ints;
|
||||
|
||||
public void Clear(int bitNum)
|
||||
{
|
||||
if (bitNum is < 0 or >= MAX_EDICTS)
|
||||
return;
|
||||
|
||||
var pInt = m_Ints + (bitNum >> LOG2_BITS_PER_INT);
|
||||
*pInt &= ~(uint)(1 << (bitNum & BITS_PER_INT - 1));
|
||||
}
|
||||
}
|
||||
public override SDType Type => SDType.GHOST;
|
||||
public override ISDInstanceLocale Locale
|
||||
=> new SoloDayLocale("Ghost War",
|
||||
"Now you see me… now you don’t! Fight through flickering visibility!");
|
||||
public override SpecialDaySettings Settings => new GhostSettings();
|
||||
|
||||
public override void Setup() {
|
||||
CHECK_TRANSMIT.Hook(onTransmit, HookMode.Post);
|
||||
Server.NextFrameAsync(() => setVisibility(false));
|
||||
base.Setup();
|
||||
}
|
||||
|
||||
public override void Execute() {
|
||||
base.Execute();
|
||||
|
||||
timeElapsed = 0f;
|
||||
setVisibility(true);
|
||||
|
||||
ghostTimer = Plugin.AddTimer(1f, () => {
|
||||
timeElapsed += 1f;
|
||||
|
||||
var mod = timeElapsed % CycleDuration;
|
||||
|
||||
var shouldBeVisible = mod < CV_VISIBLE_DURATION.Value;
|
||||
var timeLeft = (int)((shouldBeVisible ?
|
||||
CV_VISIBLE_DURATION.Value : CycleDuration) - mod);
|
||||
|
||||
if (shouldBeVisible != allPlayersVisible)
|
||||
setVisibility(shouldBeVisible);
|
||||
|
||||
foreach (var player in PlayerUtil.GetAlive()
|
||||
.Where(p => p.IsValid))
|
||||
player.PrintToCenter($"{(allPlayersVisible
|
||||
? "Visible" : "Hidden")} for: {timeLeft}s");
|
||||
}, TimerFlags.REPEAT);
|
||||
}
|
||||
|
||||
private unsafe HookResult onTransmit(DynamicHook hook) {
|
||||
if (allPlayersVisible) return HookResult.Continue;
|
||||
|
||||
var ppInfoList = (nint*)hook.GetParam<nint>(1);
|
||||
var infoCount = hook.GetParam<int>(2);
|
||||
|
||||
var players = Utilities.GetPlayers()
|
||||
.Where(p => p is { IsValid: true, PawnIsAlive: true })
|
||||
.ToList();
|
||||
|
||||
for (var i = 0; i < infoCount; i++) {
|
||||
var pInfo = ppInfoList[i];
|
||||
var slot = *(byte*)(pInfo + CHECK_TRANSMIT_PLAYER_SLOT_CACHE);
|
||||
var player = Utilities.GetPlayerFromSlot(slot);
|
||||
if (player == null || !player.IsValid) continue;
|
||||
|
||||
var info = Marshal.PtrToStructure<CCheckTransmitInfo>(pInfo);
|
||||
|
||||
foreach (var target in
|
||||
players.Where(target => target.Index != player.Index)) {
|
||||
info.m_pTransmitEntity.Clear((int)target.PlayerPawn.Value.Index);
|
||||
}
|
||||
}
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
override protected HookResult OnEnd(EventRoundEnd ev, GameEventInfo info) {
|
||||
ghostTimer?.Kill();
|
||||
CHECK_TRANSMIT.Unhook(onTransmit, HookMode.Post);
|
||||
return base.OnEnd(ev, info);
|
||||
}
|
||||
|
||||
private void setVisibility(bool state) {
|
||||
allPlayersVisible = state;
|
||||
Server.ExecuteCommand($"mp_footsteps_serverside {(state ? "1" : "0")}");
|
||||
if (state) EnableDamage(); else DisableDamage();
|
||||
foreach (var player in PlayerUtil.GetAlive()) {
|
||||
player.ExecuteClientCommand(
|
||||
$"play {(state ? "\"sounds/buttons/bell1.vsnd\"" : "\"sounds/ui/counter_beep.vsnd\"")}");
|
||||
}
|
||||
}
|
||||
|
||||
public class GhostSettings : FFASettings {
|
||||
public GhostSettings() {
|
||||
ConVarValues["mp_footsteps_serverside"] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
66
mod/Jailbreak.SpecialDay/SpecialDays/HEDay.cs
Normal file
66
mod/Jailbreak.SpecialDay/SpecialDays/HEDay.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using Jailbreak.English.SpecialDay;
|
||||
using Jailbreak.Formatting.Extensions;
|
||||
using Jailbreak.Formatting.Views.SpecialDay;
|
||||
using Jailbreak.Public.Extensions;
|
||||
using Jailbreak.Public.Mod.SpecialDay;
|
||||
using Jailbreak.Public.Mod.SpecialDay.Enums;
|
||||
using Jailbreak.Public.Utils;
|
||||
|
||||
namespace Jailbreak.SpecialDay.SpecialDays;
|
||||
|
||||
public class HEDay(BasePlugin plugin, IServiceProvider provider)
|
||||
: AbstractSpecialDay(plugin, provider), ISpecialDayMessageProvider {
|
||||
public override SDType Type => SDType.HE;
|
||||
public override SpecialDaySettings Settings => new HESettings();
|
||||
|
||||
public ISDInstanceLocale Locale
|
||||
=> new SoloDayLocale("HE Only",
|
||||
"Grenades Only—No guns. Fight against everyone else. No Camping!");
|
||||
|
||||
public override void Setup() {
|
||||
Plugin.RegisterEventHandler<EventGrenadeThrown>(onThrow);
|
||||
Timers[10] += () => Locale.BeginsIn(10).ToAllChat();
|
||||
Timers[15] += () => Locale.BeginsIn(5).ToAllChat();
|
||||
Timers[20] += Execute;
|
||||
|
||||
base.Setup();
|
||||
}
|
||||
|
||||
public override void Execute() {
|
||||
foreach (var player in PlayerUtil.GetAlive())
|
||||
player.GiveNamedItem("weapon_hegrenade");
|
||||
base.Execute();
|
||||
Locale.BeginsIn(0).ToAllChat();
|
||||
}
|
||||
|
||||
private HookResult onThrow(EventGrenadeThrown @event, GameEventInfo info) {
|
||||
var player = @event.Userid;
|
||||
if (player == null || !player.IsReal() || !player.PawnIsAlive)
|
||||
return HookResult.Continue;
|
||||
player.GiveNamedItem("weapon_hegrenade");
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
override protected HookResult
|
||||
OnEnd(EventRoundEnd @event, GameEventInfo info) {
|
||||
var result = base.OnEnd(@event, info);
|
||||
Plugin.DeregisterEventHandler<EventGrenadeThrown>(onThrow);
|
||||
return result;
|
||||
}
|
||||
|
||||
public class HESettings : SpecialDaySettings {
|
||||
public HESettings() {
|
||||
CtTeleport = TeleportType.RANDOM;
|
||||
TTeleport = TeleportType.RANDOM;
|
||||
StripToKnife = true;
|
||||
WithFriendlyFire();
|
||||
}
|
||||
|
||||
public override float FreezeTime(CCSPlayerController player) { return 1; }
|
||||
|
||||
public override ISet<string>? AllowedWeapons(CCSPlayerController player) {
|
||||
return new HashSet<string> {"weapon_hegrenade"};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ namespace Jailbreak.SpecialDay.SpecialDays;
|
||||
public class InfectionDay(BasePlugin plugin, IServiceProvider provider)
|
||||
: AbstractArmoryRestrictedDay(plugin, provider, CsTeam.CounterTerrorist),
|
||||
ISpecialDayMessageProvider {
|
||||
private readonly ICollection<int> swappedPrisoners = new HashSet<int>();
|
||||
private readonly ICollection< CCSPlayerController> swappedPrisoners = new HashSet<CCSPlayerController>();
|
||||
|
||||
public override SDType Type => SDType.INFECTION;
|
||||
|
||||
@@ -74,7 +74,7 @@ public class InfectionDay(BasePlugin plugin, IServiceProvider provider)
|
||||
target.PlayerPawn.Value!.AbsOrigin!.Clone() :
|
||||
pos;
|
||||
|
||||
swappedPrisoners.Add(player.Slot);
|
||||
swappedPrisoners.Add(player);
|
||||
if (!player.IsValid) return HookResult.Continue;
|
||||
|
||||
msg.YouWereInfectedMessage(
|
||||
@@ -107,7 +107,7 @@ public class InfectionDay(BasePlugin plugin, IServiceProvider provider)
|
||||
var hp = Settings.InitialHealth(player);
|
||||
if (hp != -1) Plugin.AddTimer(0.1f, () => { player.SetHealth(hp); });
|
||||
|
||||
var color = swappedPrisoners.Contains(player.Slot) ?
|
||||
var color = swappedPrisoners.Contains(player) ?
|
||||
Color.DarkOliveGreen :
|
||||
Color.ForestGreen;
|
||||
player.SetColor(color);
|
||||
@@ -120,13 +120,10 @@ public class InfectionDay(BasePlugin plugin, IServiceProvider provider)
|
||||
Plugin.DeregisterEventHandler<EventPlayerDeath>(onPlayerDeath);
|
||||
Plugin.DeregisterEventHandler<EventPlayerSpawn>(onRespawn);
|
||||
|
||||
Plugin.AddTimer(0.5f, () => {
|
||||
foreach (var index in swappedPrisoners) {
|
||||
var player = Utilities.GetPlayerFromSlot(index);
|
||||
if (player == null || !player.IsValid) continue;
|
||||
player.SwitchTeam(CsTeam.Terrorist);
|
||||
}
|
||||
});
|
||||
foreach (var player in swappedPrisoners) {
|
||||
if (!player.IsValid) continue;
|
||||
player.SwitchTeam(CsTeam.Terrorist);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
321
mod/Jailbreak.SpecialDay/SpecialDays/RocketJumpDay.cs
Normal file
321
mod/Jailbreak.SpecialDay/SpecialDays/RocketJumpDay.cs
Normal file
@@ -0,0 +1,321 @@
|
||||
using System.Numerics;
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Cvars.Validators;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||
using CounterStrikeSharp.API.Modules.Timers;
|
||||
using CounterStrikeSharp.API.Modules.UserMessages;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using Jailbreak.English.SpecialDay;
|
||||
using Jailbreak.Formatting.Extensions;
|
||||
using Jailbreak.Formatting.Views.SpecialDay;
|
||||
using Jailbreak.Public.Extensions;
|
||||
using Jailbreak.Public.Mod.SpecialDay;
|
||||
using Jailbreak.Public.Mod.SpecialDay.Enums;
|
||||
using Jailbreak.Public.Utils;
|
||||
using Vector = CounterStrikeSharp.API.Modules.Utils.Vector;
|
||||
|
||||
namespace Jailbreak.SpecialDay.SpecialDays;
|
||||
public class RocketJumpDay(BasePlugin plugin, IServiceProvider provider)
|
||||
: AbstractSpecialDay(plugin, provider), ISpecialDayMessageProvider {
|
||||
public static readonly FakeConVar<float> CV_BULLET_SPEED = new(
|
||||
"css_jb_rj_bullet_speed", "Speed of the projectile bullet.", 1250.0f,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<float>(100f, 5000f));
|
||||
|
||||
public static readonly FakeConVar<float> CV_MAX_DISTANCE = new(
|
||||
"css_jb_rj_max_distance", "Max distance to apply rocketjump.", 160.0f,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<float>(1f, 1000f));
|
||||
|
||||
public static readonly FakeConVar<float> CV_CLOSE_JUMP_DISTANCE = new(
|
||||
"css_jb_rj_close_jump_distance",
|
||||
"Max distance that causes a full force jump.", 37.0f,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<float>(1f, 1000f));
|
||||
|
||||
public static readonly FakeConVar<float> CV_JUMP_FORCE_MAIN = new(
|
||||
"css_jb_rj_jump_force_main", "Base jump push strength.", 270.0f,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<float>(0f, 2000f));
|
||||
|
||||
public static readonly FakeConVar<float> CV_JUMP_FORCE_UP = new(
|
||||
"css_jb_rj_jump_force_up", "Vertical boost on rocketjump.", 8.0f,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<float>(0f, 500f));
|
||||
|
||||
public static readonly FakeConVar<float> CV_JUMP_FORCE_FORWARD = new(
|
||||
"css_jb_rj_jump_force_forward", "Forward scale on rocketjump.", 1.2f,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<float>(0f, 10f));
|
||||
|
||||
public static readonly FakeConVar<float> CV_JUMP_FORCE_BACKWARD = new(
|
||||
"css_jb_rj_jump_force_backward", "Backward scale on rocketjump.", 1.25f,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<float>(0f, 10f));
|
||||
|
||||
public static readonly FakeConVar<float> CV_RUN_FORCE_MAIN = new(
|
||||
"css_jb_rj_run_force_main", "Extra boost if running.", 0.8f,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<float>(0f, 10f));
|
||||
|
||||
public static readonly FakeConVar<bool> CV_PROJ_INHERIT_PLAYER_VELOCITY = new(
|
||||
"css_jb_rj_proj_inherit_player_velocity",
|
||||
"Whether the projectile inherits player velocity. "
|
||||
+ "True allows for easier rocket jumps at the cost of 'funky' shot paths when trying to shoot a player");
|
||||
|
||||
public static readonly FakeConVar<float> CV_PROJ_DAMAGE = new(
|
||||
"css_jb_rj_proj_damage", "The damage caused by projectile explosion", 65f,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<float>(1f, 200f));
|
||||
|
||||
public static readonly FakeConVar<float> CV_PROJ_DAMAGE_RADIUS = new(
|
||||
"css_jb_rj_proj_damage_radius",
|
||||
"The radius of the explosion caused by projectile", 225f,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<float>(1f, 1000f));
|
||||
|
||||
public static readonly FakeConVar<float> CV_PROJ_GRAVITY = new(
|
||||
"css_jb_rj_proj_gravity", "The gravity of the projectile.", 0.001f,
|
||||
ConVarFlags.FCVAR_NONE, new RangeValidator<float>(0.001f, 2000f));
|
||||
|
||||
private const int GE_FIRE_BULLETS_ID = 452;
|
||||
|
||||
// Thank you https://github.com/ipsvn/cs2-Rocketjump/tree/master
|
||||
private readonly MemoryFunctionVoid<nint, nint> touch =
|
||||
new("55 48 89 E5 41 54 49 89 F4 53 48 8B 87");
|
||||
|
||||
private readonly HashSet<CCSPlayerPawn> jumping = [];
|
||||
private Dictionary<ulong, float> nextNova = new();
|
||||
|
||||
public override SDType Type => SDType.ROCKETJUMP;
|
||||
public override SpecialDaySettings Settings => new RocketJumpSettings();
|
||||
|
||||
public ISDInstanceLocale Locale
|
||||
=> new SoloDayLocale("Rocket Jump",
|
||||
"Your shotgun is now an RPG that fires grenades! "
|
||||
+ "Shoot the ground to launch! " + "Mid-air knives Insta-kill!");
|
||||
|
||||
public override void Setup() {
|
||||
Plugin.HookUserMessage(GE_FIRE_BULLETS_ID, fireBulletsUmHook);
|
||||
touch.Hook(CBaseEntity_Touch, HookMode.Pre);
|
||||
Plugin.RegisterEventHandler<EventWeaponFire>(onWeaponFire);
|
||||
VirtualFunctions.CBaseEntity_TakeDamageOldFunc.Hook(onHurt, HookMode.Pre);
|
||||
Plugin.RegisterListener<Listeners.OnTick>(onTick);
|
||||
|
||||
Timers[10] += () => Locale.BeginsIn(10).ToAllChat();
|
||||
Timers[15] += () => Locale.BeginsIn(5).ToAllChat();
|
||||
Timers[20] += () => {
|
||||
Execute();
|
||||
Locale.BeginsIn(0).ToAllChat();
|
||||
};
|
||||
|
||||
base.Setup();
|
||||
}
|
||||
|
||||
public override void Execute() {
|
||||
foreach (var player in PlayerUtil.GetAlive()) {
|
||||
player.RemoveWeapons();
|
||||
player.SetArmor(0);
|
||||
player.GiveNamedItem("weapon_knife");
|
||||
player.GiveNamedItem("weapon_nova");
|
||||
}
|
||||
base.Execute();
|
||||
}
|
||||
|
||||
override protected HookResult OnEnd(EventRoundEnd ev, GameEventInfo info) {
|
||||
Plugin.UnhookUserMessage(GE_FIRE_BULLETS_ID, fireBulletsUmHook);
|
||||
touch.Unhook(CBaseEntity_Touch, HookMode.Pre);
|
||||
Plugin.DeregisterEventHandler<EventWeaponFire>(onWeaponFire);
|
||||
VirtualFunctions.CBaseEntity_TakeDamageOldFunc.Unhook(onHurt, HookMode.Pre);
|
||||
Plugin.RemoveListener<Listeners.OnTick>(onTick);
|
||||
|
||||
// Delay to avoid mutation during hook execution
|
||||
Server.NextFrameAsync(() => { jumping.Clear(); });
|
||||
|
||||
return base.OnEnd(ev, info);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears recipients of the Nova bullet pellets to hide it from everyone.
|
||||
/// Give cleaner shot effect and removes unecessary rendering
|
||||
/// </summary>
|
||||
private HookResult fireBulletsUmHook(UserMessage um) {
|
||||
um.Recipients.Clear();
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles when the grenade touches something.
|
||||
/// Triggers a rocket jump for its owner if nearby.
|
||||
/// Uses CHEGrenadeProjectile for built-in AoE, visibility, and raycast-like behavior.
|
||||
/// This is prefered b/c using a raycast would require custom logic for:
|
||||
/// -Damage radius simulation, Entity filtering, Visual/audio, feedback Manual hit registration
|
||||
/// </summary>
|
||||
private HookResult CBaseEntity_Touch(DynamicHook hook) {
|
||||
var projectile = hook.GetParam<CHEGrenadeProjectile>(0);
|
||||
if (projectile.DesignerName != "hegrenade_projectile")
|
||||
return HookResult.Continue;
|
||||
|
||||
var owner = projectile.OwnerEntity.Value?.As<CCSPlayerPawn>();
|
||||
if (owner == null || owner.DesignerName != "player")
|
||||
return HookResult.Continue;
|
||||
|
||||
var bulletOrigin = projectile.AbsOrigin;
|
||||
var pawnOrigin = owner.AbsOrigin;
|
||||
if (bulletOrigin == null || pawnOrigin == null) return HookResult.Continue;
|
||||
|
||||
var eyeOrigin = owner.GetEyeOrigin();
|
||||
var distance = Vector3.Distance(bulletOrigin.ToVec3(), pawnOrigin.ToVec3());
|
||||
|
||||
projectile.DetonateTime = 0f;
|
||||
doJump(owner, distance, bulletOrigin.ToVec3(), eyeOrigin);
|
||||
|
||||
return HookResult.Handled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detects Nova shots and spawns a grenade projectile in the direction the player is aiming.
|
||||
/// </summary>
|
||||
private HookResult onWeaponFire(EventWeaponFire @event, GameEventInfo info) {
|
||||
var controller = @event.Userid;
|
||||
if (controller == null) return HookResult.Continue;
|
||||
|
||||
var weapon = @event.Weapon;
|
||||
if (weapon != "weapon_nova") return HookResult.Continue;
|
||||
|
||||
var sid = controller.SteamID;
|
||||
var now = Server.CurrentTime;
|
||||
|
||||
if (nextNova.TryGetValue(sid, out var next) && now < next)
|
||||
return HookResult.Continue;
|
||||
|
||||
nextNova[sid] = now + 0.82f;
|
||||
|
||||
var pawn = controller.PlayerPawn.Value;
|
||||
var origin = pawn?.AbsOrigin;
|
||||
if (pawn == null || origin == null) return HookResult.Continue;
|
||||
pawn.GetEyeForward(10.0f, out var forwardDir, out var targetPos);
|
||||
|
||||
var realBulletVelocity = forwardDir * CV_BULLET_SPEED.Value;
|
||||
var addedBulletVelocity = CV_PROJ_INHERIT_PLAYER_VELOCITY.Value ?
|
||||
pawn.AbsVelocity.ToVec3() + realBulletVelocity :
|
||||
realBulletVelocity;
|
||||
shootBullet(controller, targetPos, addedBulletVelocity,
|
||||
new Vector3(origin.X, origin.Y, (float)(origin.Z + 64.09)));
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes knife hits lethal only if the attacker is airborne via rocket jump.
|
||||
/// Nullifies Nova Pellet Damage
|
||||
/// Passes Grenades Per Usual
|
||||
/// </summary>
|
||||
private HookResult onHurt(DynamicHook hook) {
|
||||
var info = hook.GetParam<CTakeDamageInfo>(1);
|
||||
var attacker = info.Attacker.Value?.As<CCSPlayerPawn>();
|
||||
var weaponName = info.Ability.Value?.As<CCSWeaponBase>().VData?.Name;
|
||||
|
||||
if (attacker == null || weaponName == null) return HookResult.Continue;
|
||||
|
||||
if (weaponName.Contains("grenade")) return HookResult.Continue;
|
||||
|
||||
if (!weaponName.Contains("knife") && !weaponName.Contains("bayonet"))
|
||||
return HookResult.Handled;
|
||||
|
||||
if (jumping.Contains(attacker)) info.Damage = 200f;
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Continuously removes players from the "jumping" list once they land.
|
||||
/// </summary>
|
||||
private void onTick() {
|
||||
foreach (var player in jumping.Where(p => p.OnGroundLastTick).ToList())
|
||||
jumping.Remove(player);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns and launches a CHEGrenadeProjectile with explosive properties like radius and damage.
|
||||
/// </summary>
|
||||
private void shootBullet(CCSPlayerController controller, Vector3 origin,
|
||||
Vector3 velocity, Vector3 angle) {
|
||||
var pawn = controller.PlayerPawn.Value;
|
||||
if (pawn == null) return;
|
||||
|
||||
var projectile =
|
||||
Utilities
|
||||
.CreateEntityByName<CHEGrenadeProjectile>("hegrenade_projectile");
|
||||
if (projectile == null) return;
|
||||
|
||||
projectile.OwnerEntity.Raw = pawn.EntityHandle.Raw;
|
||||
projectile.Damage = CV_PROJ_DAMAGE.Value;
|
||||
projectile.DmgRadius = CV_PROJ_DAMAGE_RADIUS.Value;
|
||||
projectile.DispatchSpawn();
|
||||
projectile.AcceptInput("InitializeSpawnFromWorld", pawn, pawn);
|
||||
Schema.SetSchemaValue(projectile.Handle, "CBaseGrenade", "m_hThrower",
|
||||
pawn.EntityHandle.Raw);
|
||||
projectile.GravityScale = CV_PROJ_GRAVITY.Value;
|
||||
projectile.DetonateTime = 9999f;
|
||||
|
||||
// Set transform BY VALUE (no unsafe pointers)
|
||||
var pos = new Vector(origin.X, origin.Y, origin.Z);
|
||||
var vel = new Vector(velocity.X, velocity.Y, velocity.Z);
|
||||
var ang = new QAngle(angle.X, angle.Y, angle.Z);
|
||||
|
||||
projectile.Teleport(pos, ang, vel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates and applies rocket jump force based on distance and direction.
|
||||
/// Combines player velocity with a directional push, scaled by angle and proximity.
|
||||
/// Adds upward force and modifies Z for vertical boost.
|
||||
/// </summary>
|
||||
private void doJump(CCSPlayerPawn pawn, float distance, Vector3 bulletOrigin,
|
||||
Vector3 pawnOrigin) {
|
||||
if (distance >= CV_MAX_DISTANCE.Value) return;
|
||||
|
||||
var down = false;
|
||||
var direction = Vector3.Normalize(pawnOrigin - bulletOrigin);
|
||||
if (direction.Z < 0) down = true;
|
||||
|
||||
var pawnVelocity = pawn.AbsVelocity;
|
||||
var movementDir =
|
||||
Vector3.Normalize(new Vector3(pawnVelocity.X, pawnVelocity.Y, 0));
|
||||
|
||||
var dot = Vector3.Dot(direction, movementDir);
|
||||
var scale = dot >= 0 ?
|
||||
CV_JUMP_FORCE_FORWARD.Value :
|
||||
CV_JUMP_FORCE_BACKWARD.Value;
|
||||
|
||||
var velocity = direction * CV_JUMP_FORCE_MAIN.Value;
|
||||
var totalVelocity = (pawnVelocity.ToVec3() + velocity) * scale;
|
||||
pawnVelocity.Z = 0.0f;
|
||||
totalVelocity.Z = 0.0f;
|
||||
|
||||
if (pawn.OnGroundLastTick) totalVelocity *= CV_RUN_FORCE_MAIN.Value;
|
||||
|
||||
var forceUp = CV_JUMP_FORCE_UP.Value * (CV_MAX_DISTANCE.Value - distance);
|
||||
if (distance > CV_CLOSE_JUMP_DISTANCE.Value)
|
||||
if (totalVelocity.Z > 0.0f)
|
||||
totalVelocity.Z = 1000.0f + forceUp;
|
||||
else
|
||||
totalVelocity.Z += forceUp;
|
||||
else
|
||||
totalVelocity.Z += forceUp / 1.37f;
|
||||
if (down) velocity.Z *= -1.0f;
|
||||
unsafe { pawn.Teleport(null, null, new Vector((nint)(&totalVelocity))); }
|
||||
|
||||
jumping.Add(pawn);
|
||||
}
|
||||
|
||||
public class RocketJumpSettings : SpecialDaySettings {
|
||||
public RocketJumpSettings() {
|
||||
CtTeleport = TeleportType.RANDOM;
|
||||
TTeleport = TeleportType.RANDOM;
|
||||
StripToKnife = true;
|
||||
WithFriendlyFire();
|
||||
|
||||
ConVarValues["sv_infinite_ammo"] = 1;
|
||||
ConVarValues["mp_death_drop_gun"] = 0;
|
||||
ConVarValues["ff_damage_reduction_grenade_self"] = 0f;
|
||||
ConVarValues["sv_falldamage_scale"] = 0f;
|
||||
}
|
||||
|
||||
public override float FreezeTime(CCSPlayerController player) { return 1; }
|
||||
}
|
||||
}
|
||||
160
mod/Jailbreak.Warden/Commands/CountdownCommandBehavior.cs
Normal file
160
mod/Jailbreak.Warden/Commands/CountdownCommandBehavior.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
using System;
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Attributes.Registration;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using Jailbreak.Formatting.Extensions;
|
||||
using Jailbreak.Formatting.Views;
|
||||
using Jailbreak.Formatting.Views.Warden;
|
||||
using Jailbreak.Public.Behaviors;
|
||||
using Jailbreak.Public.Mod.Mute;
|
||||
using Jailbreak.Public.Mod.Warden;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using Jailbreak.Formatting.Base;
|
||||
using Jailbreak.Formatting.Core;
|
||||
using Jailbreak.Formatting.Objects;
|
||||
|
||||
public class CountdownCommandBehavior(IWardenService warden, IMuteService mute,
|
||||
IWardenLocale wardenLocale, IGenericCmdLocale generics) : IPluginBehavior {
|
||||
|
||||
public static readonly FakeConVar<int> CV_WARDEN_MAX_COUNTDOWN =
|
||||
new("css_jb_warden_countdown_max",
|
||||
"The maximum duration for a countdown", 15);
|
||||
|
||||
public static readonly FakeConVar<int> CV_WARDEN_MIN_COUNTDOWN =
|
||||
new("css_jb_warden_countdown_min",
|
||||
"The minimum duration for a countdown", 3);
|
||||
|
||||
public static readonly FakeConVar<int> CV_WARDEN_DEFAULT_COUNTDOWN =
|
||||
new("css_jb_warden_countdown_default",
|
||||
"The default duration for a countdown", 5);
|
||||
|
||||
private DateTime lastCountdown = DateTime.MinValue;
|
||||
private int countdownDuration;
|
||||
|
||||
[ConsoleCommand("css_countdown",
|
||||
"Invokes a countdown "
|
||||
+ "that will display in chat and notify Ts when to go (for a game or to follow a command) "
|
||||
+ "If no duration is provided it will default to 5 seconds. "
|
||||
+ "Maximum duration of 15 seconds. Minimum duration of 3 seconds.")]
|
||||
|
||||
public void Command_Countdown(CCSPlayerController? executor, CommandInfo command) {
|
||||
// Set duration of countdown
|
||||
countdownDuration = CV_WARDEN_DEFAULT_COUNTDOWN.Value;
|
||||
|
||||
// Ensure countdown duration is within acceptable range
|
||||
if (command.ArgCount == 2) {
|
||||
if (!int.TryParse(command.GetArg(1), out countdownDuration)) {
|
||||
generics.InvalidParameter(command.GetArg(1), "number");
|
||||
command.ReplyToCommand("Expected a number parameter.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (countdownDuration <= 0) {
|
||||
generics.InvalidParameter(command.GetArg(1), "number greater than 0");
|
||||
command.ReplyToCommand("Expected a number greater than 0.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (countdownDuration < CV_WARDEN_MIN_COUNTDOWN.Value) {
|
||||
generics.InvalidParameter(command.GetArg(1),
|
||||
$"number greater than or equal to {CV_WARDEN_MIN_COUNTDOWN.Value}");
|
||||
command.ReplyToCommand($"Expected a number greater than or equal to {CV_WARDEN_MIN_COUNTDOWN.Value}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (countdownDuration > CV_WARDEN_MAX_COUNTDOWN.Value) {
|
||||
generics.InvalidParameter(command.GetArg(1),
|
||||
$"number less than or equal to {CV_WARDEN_MAX_COUNTDOWN.Value}");
|
||||
command.ReplyToCommand($"Expected a number less than or equal to {CV_WARDEN_MAX_COUNTDOWN.Value}");
|
||||
return;
|
||||
}
|
||||
//
|
||||
|
||||
// Check perm and enact peace
|
||||
bool success = PermCheckAndEnactPeace(executor);
|
||||
if (!success) return;
|
||||
|
||||
// Inform players of countdown
|
||||
StartCountDown(countdownDuration);
|
||||
|
||||
// Create callbacks each second less than 5 or divisible by 5 to notify players of countdown time remaining / completion of countdown
|
||||
for (int i = countdownDuration; i > 0; --i) {
|
||||
int current = i; // lambda capture
|
||||
|
||||
if (current <= 5 || current % 5 == 0) {
|
||||
Server.RunOnTick(Server.TickCount + (64 * (countdownDuration - current)),
|
||||
() => PrintCountdownToPlayers(current));
|
||||
}
|
||||
}
|
||||
Server.RunOnTick(Server.TickCount + (64 * countdownDuration), () => PrintGoToPlayers());
|
||||
}
|
||||
|
||||
// Is this okay?
|
||||
// Feels like bad encapsulation
|
||||
private static readonly FormatObject PREFIX =
|
||||
new HiddenFormatObject($" {ChatColors.DarkBlue}Countdown>") {
|
||||
Plain = false, Panorama = false, Chat = true
|
||||
};
|
||||
|
||||
private void StartCountDown(int duration) {
|
||||
new SimpleView { PREFIX, $"A {duration} second countdown has begun!" }.ToAllChat();
|
||||
}
|
||||
|
||||
private void PrintCountdownToPlayers(int seconds) {
|
||||
new SimpleView { PREFIX, seconds.ToString() }.ToAllChat();
|
||||
|
||||
// var players = Utilities.GetPlayers();
|
||||
// foreach (var player in players) {
|
||||
// player.ExecuteClientCommand("play buttons\\blip1");
|
||||
// }
|
||||
}
|
||||
|
||||
private void PrintGoToPlayers() {
|
||||
new SimpleView { PREFIX, "GO! GO! GO!" }.ToAllChat();
|
||||
|
||||
// var players = Utilities.GetPlayers();
|
||||
// foreach (var player in players) {
|
||||
// player.ExecuteClientCommand("play \\sounds\\vo\\agents\\balkan\\radio_letsgo01.vsnd_c");
|
||||
// }
|
||||
}
|
||||
//
|
||||
|
||||
// Check permissions and attempt to enact a period of peace for players to focus on the countdown
|
||||
private bool PermCheckAndEnactPeace(CCSPlayerController? executor) {
|
||||
var fromWarden = executor != null && warden.IsWarden(executor);
|
||||
|
||||
if (executor == null
|
||||
|| AdminManager.PlayerHasPermissions(executor, "@css/cheats")) {
|
||||
// Server console or a high-admin is invoking the peace period, bypass cooldown
|
||||
mute.PeaceMute(MuteReason.ADMIN);
|
||||
lastCountdown = DateTime.Now;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!fromWarden
|
||||
&& AdminManager.PlayerHasPermissions(executor, "@css/chat")) {
|
||||
mute.PeaceMute(MuteReason.ADMIN);
|
||||
lastCountdown = DateTime.Now;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (DateTime.Now - lastCountdown < TimeSpan.FromSeconds(60)) {
|
||||
generics.CommandOnCooldown(lastCountdown.AddSeconds(60))
|
||||
.ToChat(executor);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fromWarden) {
|
||||
mute.PeaceMute(fromWarden ? MuteReason.WARDEN_INVOKED : MuteReason.ADMIN);
|
||||
lastCountdown = DateTime.Now;
|
||||
return true;
|
||||
} else {
|
||||
wardenLocale.NotWarden.ToChat(executor);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,12 @@ public class SpecialTreatmentCommandsBehavior(IWardenService warden,
|
||||
// TODO: Pop up menu of prisoners to toggle ST for
|
||||
return;
|
||||
|
||||
// FIXME: Remove after @aim is fixed in CSS
|
||||
if (command.ArgByIndex(1).ToLower().Contains("@aim")) {
|
||||
command.ReplyToCommand("@aim is currently not supported due to a bug in CSS.");
|
||||
return;
|
||||
}
|
||||
|
||||
var targets = command.GetArgTargetResult(1);
|
||||
var eligible = targets
|
||||
.Where(p => p is { Team: CsTeam.Terrorist, PawnIsAlive: true })
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CS2ScreenMenuAPI" Version="2.6.0" />
|
||||
<PackageReference Include="CS2TraceRay" Version="1.0.8" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -10,6 +10,8 @@ using CounterStrikeSharp.API.Modules.Utils;
|
||||
using CS2ScreenMenuAPI;
|
||||
using CS2ScreenMenuAPI.Enums;
|
||||
using CS2ScreenMenuAPI.Internal;
|
||||
using CS2TraceRay.Class;
|
||||
using CS2TraceRay.Enum;
|
||||
using Jailbreak.Formatting.Extensions;
|
||||
using Jailbreak.Formatting.Views.Warden;
|
||||
using Jailbreak.Public;
|
||||
@@ -126,8 +128,12 @@ public class WardenMarkerBehavior(IWardenService warden, IWardenLocale locale)
|
||||
|| weapon == "weapon_sg556")
|
||||
return;
|
||||
|
||||
var position = RayTrace.FindRayTraceIntersection(warden.Warden);
|
||||
if (position == null) return;
|
||||
var trace =
|
||||
warden.Warden.GetGameTraceByEyePosition(TraceMask.MaskSolid, Contents.Solid,
|
||||
warden.Warden);
|
||||
if (trace == null) return;
|
||||
|
||||
var position = trace.Value.Position.ToCsVector();
|
||||
|
||||
if (!activelyPlacing) {
|
||||
for (var i = 0; i < markers.Length; i++) {
|
||||
|
||||
@@ -1,203 +0,0 @@
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using Jailbreak.Public.Extensions;
|
||||
using Vector = CounterStrikeSharp.API.Modules.Utils.Vector;
|
||||
|
||||
namespace Jailbreak.Warden.Paint;
|
||||
// https://github.com/zakriamansoor47/SLAYER_LineOfSight/blob/main/RayTrace.cs
|
||||
|
||||
// Used these to remove compile warnings
|
||||
#pragma warning disable CS8618
|
||||
#pragma warning disable CS8602
|
||||
#pragma warning disable CS8603
|
||||
public static class RayTrace {
|
||||
private static TraceShapeDelegate _traceShape;
|
||||
|
||||
private static readonly nint TraceFunc = NativeAPI.FindSignature(
|
||||
Addresses.ServerPath,
|
||||
Environment.OSVersion.Platform == PlatformID.Unix ?
|
||||
"48 B8 ? ? ? ? ? ? ? ? 55 48 89 E5 41 57 41 56 49 89 D6 41 55" :
|
||||
"4C 8B DC 49 89 5B ? 49 89 6B ? 49 89 73 ? 57 41 56 41 57 48 81 EC ? ? ? ? 0F 57 C0");
|
||||
|
||||
private static readonly nint GameTraceManager = NativeAPI.FindSignature(
|
||||
Addresses.ServerPath,
|
||||
Environment.OSVersion.Platform == PlatformID.Unix ?
|
||||
"48 8D 05 ? ? ? ? F3 0F 58 8D ? ? ? ? 31 FF" :
|
||||
"48 8B 0D ? ? ? ? 48 8D 45 ? 48 89 44 24 ? 4C 8D 44 24 ? C7 44 24 ? ? ? ? ? 48 8D 54 24 ? 4C 8B CB");
|
||||
|
||||
public static Vector TraceShape(Vector _origin, QAngle _viewangles,
|
||||
bool fromPlayer = false) {
|
||||
var _forward = new Vector();
|
||||
|
||||
// Get forward vector from view angles
|
||||
NativeAPI.AngleVectors(_viewangles.Handle, _forward.Handle, 0, 0);
|
||||
var _endOrigin = new Vector(_origin.X + _forward.X * 8192,
|
||||
_origin.Y + _forward.Y * 8192, _origin.Z + _forward.Z * 8192);
|
||||
|
||||
var d = 50;
|
||||
|
||||
if (fromPlayer) {
|
||||
_origin.X += _forward.X * d;
|
||||
_origin.Y += _forward.Y * d;
|
||||
_origin.Z += _forward.Z * d + 64;
|
||||
}
|
||||
|
||||
return TraceShape(_origin, _endOrigin);
|
||||
}
|
||||
|
||||
public static unsafe Vector TraceShape(Vector? _origin, Vector _endOrigin) {
|
||||
try {
|
||||
var _gameTraceManagerAddress =
|
||||
Address.GetAbsoluteAddress(GameTraceManager, 3, 7);
|
||||
|
||||
_traceShape =
|
||||
Marshal.GetDelegateForFunctionPointer<TraceShapeDelegate>(TraceFunc);
|
||||
|
||||
var _trace = stackalloc GameTrace[1];
|
||||
|
||||
ulong mask = 0x1C1003;
|
||||
var result = _traceShape(*(nint*)_gameTraceManagerAddress, _origin.Handle,
|
||||
_endOrigin.Handle, 0, mask, 4, _trace);
|
||||
|
||||
var endPos = new Vector(_trace->EndPos.X, _trace->EndPos.Y,
|
||||
_trace->EndPos.Z);
|
||||
|
||||
if (result) return endPos;
|
||||
} catch (Exception ex) {
|
||||
Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
private unsafe delegate bool TraceShapeDelegate(nint GameTraceManager,
|
||||
nint vecStart, nint vecEnd, nint skip, ulong mask, byte a6,
|
||||
GameTrace* pGameTrace);
|
||||
|
||||
public static Vector? FindRayTraceIntersection(CCSPlayerController player) {
|
||||
var camera = player.Pawn.Value?.CameraServices;
|
||||
if (camera == null || player.PlayerPawn.Value == null) return null;
|
||||
var pawn = player.Pawn.Value;
|
||||
if (pawn == null || !pawn.IsValid || !player.PlayerPawn.Value.IsValid
|
||||
|| pawn.CameraServices == null)
|
||||
return null;
|
||||
if (pawn.AbsOrigin == null) return null;
|
||||
return RayTrace.TraceShape(pawn.AbsOrigin.Clone(),
|
||||
player.PlayerPawn.Value.EyeAngles, true);
|
||||
}
|
||||
}
|
||||
|
||||
internal static class Address {
|
||||
public static unsafe nint
|
||||
GetAbsoluteAddress(nint addr, nint offset, int size) {
|
||||
if (addr == IntPtr.Zero)
|
||||
throw new Exception("Failed to find RayTrace signature.");
|
||||
|
||||
var code = *(int*)(addr + offset);
|
||||
return addr + code + size;
|
||||
}
|
||||
|
||||
public static nint GetCallAddress(nint a) {
|
||||
return GetAbsoluteAddress(a, 1, 5);
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x35)]
|
||||
public struct Ray {
|
||||
[FieldOffset(0)]
|
||||
public Vector3 Start;
|
||||
|
||||
[FieldOffset(0xC)]
|
||||
public Vector3 End;
|
||||
|
||||
[FieldOffset(0x18)]
|
||||
public Vector3 Mins;
|
||||
|
||||
[FieldOffset(0x24)]
|
||||
public Vector3 Maxs;
|
||||
|
||||
[FieldOffset(0x34)]
|
||||
public byte UnkType;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x44)]
|
||||
public struct TraceHitboxData {
|
||||
[FieldOffset(0x38)]
|
||||
public int HitGroup;
|
||||
|
||||
[FieldOffset(0x40)]
|
||||
public int HitboxId;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0xB8)]
|
||||
public unsafe struct GameTrace {
|
||||
[FieldOffset(0)]
|
||||
public void* Surface;
|
||||
|
||||
[FieldOffset(0x8)]
|
||||
public void* HitEntity;
|
||||
|
||||
[FieldOffset(0x10)]
|
||||
public TraceHitboxData* HitboxData;
|
||||
|
||||
[FieldOffset(0x50)]
|
||||
public uint Contents;
|
||||
|
||||
[FieldOffset(0x78)]
|
||||
public Vector3 StartPos;
|
||||
|
||||
[FieldOffset(0x84)]
|
||||
public Vector3 EndPos;
|
||||
|
||||
[FieldOffset(0x90)]
|
||||
public Vector3 Normal;
|
||||
|
||||
[FieldOffset(0x9C)]
|
||||
public Vector3 Position;
|
||||
|
||||
[FieldOffset(0xAC)]
|
||||
public float Fraction;
|
||||
|
||||
[FieldOffset(0xB6)]
|
||||
public bool AllSolid;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x3a)]
|
||||
public unsafe struct TraceFilter {
|
||||
[FieldOffset(0)]
|
||||
public void* Vtable;
|
||||
|
||||
[FieldOffset(0x8)]
|
||||
public ulong Mask;
|
||||
|
||||
[FieldOffset(0x20)]
|
||||
public fixed uint SkipHandles[4];
|
||||
|
||||
[FieldOffset(0x30)]
|
||||
public fixed ushort arrCollisions[2];
|
||||
|
||||
[FieldOffset(0x34)]
|
||||
public uint Unk1;
|
||||
|
||||
[FieldOffset(0x38)]
|
||||
public byte Unk2;
|
||||
|
||||
[FieldOffset(0x39)]
|
||||
public byte Unk3;
|
||||
}
|
||||
|
||||
public unsafe struct TraceFilterV2 {
|
||||
public ulong Mask;
|
||||
public fixed ulong V1[2];
|
||||
public fixed uint SkipHandles[4];
|
||||
public fixed ushort arrCollisions[2];
|
||||
public short V2;
|
||||
public byte V3;
|
||||
public byte V4;
|
||||
public byte V5;
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Attributes.Registration;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using CS2TraceRay.Class;
|
||||
using CS2TraceRay.Enum;
|
||||
using GangsAPI.Data;
|
||||
using GangsAPI.Services.Gang;
|
||||
using GangsAPI.Services.Player;
|
||||
@@ -14,6 +17,7 @@ using Jailbreak.Public.Mod.Rainbow;
|
||||
using Jailbreak.Public.Mod.Warden;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using WardenPaintColorPerk;
|
||||
using Vector = CounterStrikeSharp.API.Modules.Utils.Vector;
|
||||
|
||||
namespace Jailbreak.Warden.Paint;
|
||||
|
||||
@@ -47,8 +51,12 @@ public class WardenPaintBehavior(IWardenService wardenService,
|
||||
|
||||
if ((warden.Buttons & PlayerButtons.Use) == 0) return;
|
||||
|
||||
var position = RayTrace.FindRayTraceIntersection(warden);
|
||||
if (position == null) return;
|
||||
var trace =
|
||||
warden.GetGameTraceByEyePosition(TraceMask.MaskSolid, Contents.TouchAll,
|
||||
warden);
|
||||
if (trace == null) return;
|
||||
|
||||
var position = trace.Value.Position.ToCsVector();
|
||||
|
||||
var start = lastPosition ?? position;
|
||||
start = start.Clone();
|
||||
|
||||
121
mod/Jailbreak.Warden/Selection/AutoWarden.cs
Normal file
121
mod/Jailbreak.Warden/Selection/AutoWarden.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
using System.Collections.Concurrent;
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Attributes.Registration;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using Jailbreak.Formatting.Extensions;
|
||||
using Jailbreak.Formatting.Views;
|
||||
using Jailbreak.Formatting.Views.Warden;
|
||||
using Jailbreak.Public;
|
||||
using Jailbreak.Public.Behaviors;
|
||||
using Jailbreak.Public.Extensions;
|
||||
using Jailbreak.Public.Mod.Warden;
|
||||
using MAULActainShared.plugin.models;
|
||||
|
||||
namespace Jailbreak.Warden.Selection;
|
||||
|
||||
public class AutoWarden(IWardenSelectionService selectionService,
|
||||
IWardenLocale locale, IGenericCmdLocale generic) : IPluginBehavior {
|
||||
|
||||
private static readonly ConcurrentDictionary<ulong, bool> CACHED_COOKIES = new();
|
||||
|
||||
private static readonly FakeConVar<string> CV_AUTOWARDEN_FLAG =
|
||||
new("css_autowarden_flag", "Permission flag required to enable auto-Warden",
|
||||
"@ego/dssilver");
|
||||
private static readonly FakeConVar<float> CV_AUTOWARDEN_DELAY_INTERVAL =
|
||||
new("css_autowarden_delay_interval", "The amount of time in seconds to wait after round start to queue users with auto-warden enabled for warden",
|
||||
5f);
|
||||
|
||||
private BasePlugin plugin = null!;
|
||||
private ICookie? cookie;
|
||||
|
||||
public void Start(BasePlugin basePlugin) {
|
||||
plugin = basePlugin;
|
||||
|
||||
TryLoadCookie();
|
||||
basePlugin.RegisterListener<Listeners.OnMapStart>(OnMapStart);
|
||||
basePlugin.RegisterEventHandler<EventRoundPoststart>(OnRoundStart);
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
private void OnMapStart(string mapname) {
|
||||
// Attempt to load the cookie OnMapStart if it fails to load on plugin start
|
||||
// This can happen if the MAUL plugin is loaded *after* this plugin
|
||||
if (cookie == null) TryLoadCookie();
|
||||
else plugin.RemoveListener<Listeners.OnMapStart>(OnMapStart);
|
||||
}
|
||||
|
||||
private HookResult OnRoundStart(EventRoundPoststart @event, GameEventInfo info) {
|
||||
plugin.AddTimer(CV_AUTOWARDEN_DELAY_INTERVAL.Value, () => {
|
||||
foreach (var player in Utilities.GetPlayers()
|
||||
.Where(p => p.Team == CsTeam.CounterTerrorist
|
||||
&& p.IsReal()
|
||||
&& p.PawnIsAlive
|
||||
&& AdminManager.PlayerHasPermissions(p, CV_AUTOWARDEN_FLAG.Value))) {
|
||||
|
||||
if (player.PlayerPawn.Value == null
|
||||
|| !player.PlayerPawn.Value.HasMovedSinceSpawn)
|
||||
continue;
|
||||
|
||||
var steam = player.SteamID;
|
||||
if (!CACHED_COOKIES.ContainsKey(steam))
|
||||
Task.Run(async () => await populateCache(player, steam));
|
||||
|
||||
if (!CACHED_COOKIES.TryGetValue(steam, out var value) || !value)
|
||||
continue;
|
||||
selectionService.TryEnter(player);
|
||||
locale.JoinRaffle.ToChat(player);
|
||||
}
|
||||
});
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
[ConsoleCommand("css_aw")]
|
||||
[ConsoleCommand("css_autowarden")]
|
||||
public void Command_AutoWarden(CCSPlayerController? player, CommandInfo info) {
|
||||
if (player == null) return;
|
||||
if (!AdminManager.PlayerHasPermissions(player, CV_AUTOWARDEN_FLAG.Value)) {
|
||||
generic.NoPermissionMessage(CV_AUTOWARDEN_FLAG.Value).ToChat(player);
|
||||
return;
|
||||
}
|
||||
if (cookie == null) { locale.TogglingNotEnabled.ToChat(player); return; }
|
||||
|
||||
var steam = player.SteamID;
|
||||
Task.Run(async () => {
|
||||
var cur = await cookie.Get(steam);
|
||||
var enable = cur != "Y";
|
||||
await cookie.Set(steam, enable ? "Y" : "N");
|
||||
await Server.NextFrameAsync(() => {
|
||||
if (!player.IsValid) return;
|
||||
locale.AutoWardenToggled(enable).ToChat(player);
|
||||
CACHED_COOKIES[steam] = enable;
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void TryLoadCookie() {
|
||||
Task.Run(async () => {
|
||||
if (API.Actain != null)
|
||||
cookie = await API.Actain.getCookieService()
|
||||
.RegClientCookie("jb_warden_auto");
|
||||
});
|
||||
}
|
||||
|
||||
private async Task populateCache(CCSPlayerController player, ulong steam) {
|
||||
if (cookie == null) return;
|
||||
var val = await cookie.Get(steam);
|
||||
var enabled = val == "Y";
|
||||
CACHED_COOKIES[steam] = enabled;
|
||||
if (!enabled) return;
|
||||
await Server.NextFrameAsync(() => {
|
||||
if (!player.IsValid) return;
|
||||
selectionService.TryEnter(player);
|
||||
locale.JoinRaffle.ToChat(player);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -23,10 +23,11 @@ public static class WardenServiceExtension {
|
||||
serviceCollection.AddPluginBehavior<IWardenIcon, WardenIconBehavior>();
|
||||
serviceCollection.AddPluginBehavior<ISpecialIcon, SpecialIconBehavior>();
|
||||
serviceCollection.AddPluginBehavior<CountCommandsBehavior>();
|
||||
|
||||
serviceCollection.AddPluginBehavior<AutoWarden>();
|
||||
|
||||
serviceCollection.AddPluginBehavior<SpecialTreatmentCommandsBehavior>();
|
||||
serviceCollection.AddPluginBehavior<PeaceCommandsBehavior>();
|
||||
serviceCollection.AddPluginBehavior<CountdownCommandBehavior>();
|
||||
serviceCollection.AddPluginBehavior<WardenCommandsBehavior>();
|
||||
serviceCollection.AddPluginBehavior<RollCommandBehavior>();
|
||||
serviceCollection.AddPluginBehavior<ChickenCommandBehavior>();
|
||||
|
||||
@@ -18,6 +18,8 @@ public interface IWardenLocale {
|
||||
public IView FireCommandFailed { get; }
|
||||
|
||||
public IView CannotWardenDuringWarmup { get; }
|
||||
|
||||
public IView TogglingNotEnabled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a view for when the specified player passes warden
|
||||
@@ -51,4 +53,6 @@ public interface IWardenLocale {
|
||||
public IView MarkerPlaced(string marker);
|
||||
|
||||
public IView MarkerRemoved(string marker);
|
||||
|
||||
IView AutoWardenToggled(bool enabled);
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using CounterStrikeSharp.API.Modules.UserMessages;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using Vector = CounterStrikeSharp.API.Modules.Utils.Vector;
|
||||
|
||||
namespace Jailbreak.Public.Extensions;
|
||||
|
||||
@@ -179,4 +181,31 @@ public static class PlayerExtensions {
|
||||
color.R | color.G << 8 | color.B << 16 | color.A << 24);
|
||||
fadeMsg.Send(player);
|
||||
}
|
||||
|
||||
public static Vector3 GetEyeOrigin(this CCSPlayerPawn pawn) {
|
||||
var origin = pawn.AbsOrigin;
|
||||
if (origin == null) return Vector3.Zero;
|
||||
|
||||
//return new Vector3(origin.X, origin.Y,
|
||||
//origin.Z + pawn.CameraServices?.OldPlayerViewOffsetZ ?? 0.0f);
|
||||
|
||||
return new Vector3(origin.X, origin.Y,
|
||||
origin.Z + 64.09f);
|
||||
}
|
||||
|
||||
public static void GetEyeForward(this CCSPlayerPawn pawn, float distance,
|
||||
out Vector3 forward, out Vector3 target) {
|
||||
if (pawn.AbsOrigin == null) {
|
||||
forward = default;
|
||||
target = default;
|
||||
return;
|
||||
}
|
||||
|
||||
var angles = new Vector3(pawn.AbsOrigin.X, pawn.AbsOrigin.Y,
|
||||
pawn.AbsOrigin.Z + 64.09f);
|
||||
angles.AngleVectors(out forward, out _, out _);
|
||||
|
||||
var eyeOrigin = pawn.GetEyeOrigin();
|
||||
target = eyeOrigin + forward * distance;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using System.Numerics;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using Vector = CounterStrikeSharp.API.Modules.Utils.Vector;
|
||||
|
||||
namespace Jailbreak.Public.Extensions;
|
||||
|
||||
@@ -50,4 +52,46 @@ public static class VectorExtensions {
|
||||
HorizontalDistanceSquared(this Vector vector, Vector other) {
|
||||
return MathF.Pow(vector.X - other.X, 2) + MathF.Pow(vector.Y - other.Y, 2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a CounterStrikeSharp Vector Into a Vector3 Class
|
||||
/// </summary>
|
||||
/// <param name="vector"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector3 ToVec3(this Vector vector) {
|
||||
return new Vector3(vector.X, vector.Y, vector.Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Vector3 Into a CounterStrikeSharp Vector Class
|
||||
/// </summary>
|
||||
/// <param name="vec3"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector ToCsVector(this Vector3 vec3) {
|
||||
return new Vector(vec3.X, vec3.Y, vec3.Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the given angle vector (pitch, yaw, roll) into directional unit vectors:
|
||||
/// forward, right, and up.
|
||||
/// Useful for translating eye angles or view angles into world-space directions.
|
||||
/// Wraps the native `AngleVectors` call from the engine.
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="forward"></param>
|
||||
/// <param name="right"></param>
|
||||
/// <param name="up"></param>
|
||||
public static void AngleVectors(this Vector3 input, out Vector3 forward,
|
||||
out Vector3 right, out Vector3 up) {
|
||||
Vector3 tmpForward, tmpRight, tmpUp;
|
||||
|
||||
unsafe {
|
||||
NativeAPI.AngleVectors((nint)(&input), (nint)(&tmpForward),
|
||||
(nint)(&tmpRight), (nint)(&tmpUp));
|
||||
}
|
||||
|
||||
forward = tmpForward;
|
||||
right = tmpRight;
|
||||
up = tmpUp;
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,11 @@
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.296"/>
|
||||
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.330" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2024.2.0"/>
|
||||
<PackageReference Include="System.Text.Json" Version="8.0.5"/>
|
||||
<ProjectReference Include="..\Jailbreak.Tag\Jailbreak.Tag.csproj"/>
|
||||
|
||||
@@ -51,7 +51,7 @@ public abstract class AbstractSpecialDay(BasePlugin plugin,
|
||||
/// </summary>
|
||||
public virtual void Setup() {
|
||||
Plugin.RegisterFakeConVars(this);
|
||||
Plugin.RegisterEventHandler<EventRoundEnd>(OnEnd);
|
||||
Plugin.RegisterEventHandler<EventRoundEnd>(OnEnd, HookMode.Pre);
|
||||
|
||||
foreach (var entry in Settings.ConVarValues) {
|
||||
var cv = ConVar.Find(entry.Key);
|
||||
@@ -274,7 +274,7 @@ public abstract class AbstractSpecialDay(BasePlugin plugin,
|
||||
|
||||
previousConvarValues.Clear();
|
||||
|
||||
Plugin.DeregisterEventHandler<EventRoundEnd>(OnEnd);
|
||||
Plugin.DeregisterEventHandler<EventRoundEnd>(OnEnd, HookMode.Pre);
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,13 +8,16 @@ public enum SDType {
|
||||
CUSTOM,
|
||||
BHOP,
|
||||
FFA,
|
||||
FOG,
|
||||
GUNGAME,
|
||||
GHOST,
|
||||
HE,
|
||||
HNS,
|
||||
INFECTION,
|
||||
NOSCOPE,
|
||||
OITC,
|
||||
PACMAN,
|
||||
ROCKETJUMP,
|
||||
SNAKE,
|
||||
SPEEDRUN,
|
||||
TAG,
|
||||
@@ -34,8 +37,12 @@ public static class SDTypeExtensions {
|
||||
"tron" => SDType.SNAKE,
|
||||
"gun" => SDType.GUNGAME,
|
||||
"zomb" or "zombie" => SDType.INFECTION,
|
||||
"rocket" or "rj" or "marketgardner" => SDType.ROCKETJUMP,
|
||||
"speed" or "speeders" or "speedrunners" or "race" => SDType.SPEEDRUN,
|
||||
"tp" => SDType.TELEPORT,
|
||||
"he" or "grenade" or "grenades" => SDType.HE,
|
||||
"ghost" or "ghosts" => SDType.GHOST,
|
||||
"fog" => SDType.FOG,
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user