157 Commits

Author SHA1 Message Date
ShookEagle
400a5171a7 Temporarily disable RJ for further Testing 2025-09-29 22:09:48 -05:00
ShookEagle
edd6dfdab6 Fix Eye Foward Method 2025-09-29 21:58:20 -05:00
ShookEagle
8bdfff38e5 Change Content Mask 2025-09-29 21:43:46 -05:00
ShookEagle
d5f8c80b0d Bump CS2TraceRay 1.0.7 -> 1.0.8 2025-09-28 16:15:17 -05:00
MSWS
6bc0395768 Build 2025-09-22 21:48:37 -07:00
Reece
a4b5394e0b Fix/various fixes (#398)
Changelogs:
- Switch to CS2TraceRay by schwarper
- Speculative Fix Paint
- Speculative Fix Rocket Jump Firing
- Speculative Fix LR Immunity
2025-09-15 16:38:53 -07:00
ShookEagle
1434b42e79 Remove TODO 2025-09-15 16:35:04 -05:00
ShookEagle
6d01e2e58d Use SteamID for participant identity (not Equals) In attempt to fix relentless damage blocking 2025-09-15 16:32:20 -05:00
ShookEagle
4de90e6ae9 Remove TODO 2025-09-15 16:03:44 -05:00
ShookEagle
981025facd Speculative Rocket Jump Fix 2025-09-15 16:03:24 -05:00
ShookEagle
2c50333446 Remove TODO 2025-09-15 15:57:38 -05:00
ShookEagle
d2db34b509 TODOs 2025-09-15 15:56:59 -05:00
ShookEagle
53e75cdc22 add Vec3 -> Vec Extension 2025-09-15 15:56:36 -05:00
ShookEagle
0cb7f833dc Convert To RayTrace Usage 2025-09-15 15:53:09 -05:00
ShookEagle
5fdefd67ee Switch to TraceRay library to remedy outdated sigs 2025-09-15 15:38:51 -05:00
ShookEagle
04c383916c Trigger Build 2025-08-19 20:34:25 -05:00
ShookEagle
b78b05c73f Speculative fix for Projectils spawning at (0,0,0) 2025-08-19 19:53:08 -05:00
ShookEagle
4f121106f9 Formatting and Cleanup 2025-08-10 08:07:58 -05:00
ShookEagle
d57cefaad2 Fix water spam by restricting time between shots 2025-08-08 14:25:45 -05:00
ShookEagle
d91cede2a8 Try .75 2025-08-08 13:20:04 -05:00
ShookEagle
588b9748c4 60/68 2025-08-08 12:49:32 -05:00
ShookEagle
8ba25a410e Hard code shot delay 2025-08-08 12:32:27 -05:00
ShookEagle
7a60ff7c6d more speculative fixing 2025-08-08 12:16:22 -05:00
ShookEagle
74e20acfe3 Disallow firing if eyes are underwater 2025-08-08 11:44:35 -05:00
ShookEagle
900e79401f more fixing 2025-08-08 11:12:32 -05:00
ShookEagle
a40a0841ea more speculative fixing 2025-08-08 11:07:03 -05:00
ShookEagle
b165823621 Speculative fix for underwater shooting 2025-08-08 10:44:53 -05:00
Reece
19215e2695 Feature: Auto-Warden DS Perk (#397)
Auto Warden DS Perk
- DS Perk that auto queues a player for warden if they are on the CT
Team
- Available to DS-Silver+
- Use `css_autowarden` and `css_aw` to enable auto warden
- Will not queue AFK players for Warden
- Default Value: `Disabled`
2025-08-08 10:22:52 -05:00
ShookEagle
c5d050c226 add css_aw alias 2025-08-08 10:06:26 -05:00
ShookEagle
5957833e81 Make default Auto Warden Value False 2025-08-08 10:00:27 -05:00
ShookEagle
cecf3cec04 Try populate cache if failed 2025-08-08 09:56:28 -05:00
ShookEagle
94db4d7252 Merge remote-tracking branch 'origin/dev' into feat/autowarden 2025-08-08 09:47:22 -05:00
ShookEagle
af623b2ea5 Buff RJ Grenade from 57 -> 65 2025-08-06 18:52:10 -05:00
ShookEagle
b4aeb9b8ee Grammer 2025-08-06 17:45:30 -05:00
ShookEagle
5213241dfc Reverse it lol 2025-08-06 15:17:10 -05:00
ShookEagle
ec45ba581f use FFASetting 2025-08-06 15:08:00 -05:00
Reece
a832e43230 Fix/sd fixes (#395)
Changes:
- Potentially allow nova to shoot underwater
- Switch to lower level schema for `CheckTransmit` to fix Ghost War
Invisibility.
2025-08-06 14:58:59 -05:00
ShookEagle
3ec269eef0 Switch to Lower level CheckTransmit Schema to allow simultaneous function with HidePlayers Plugin 2025-08-06 14:54:33 -05:00
ShookEagle
5a6e1a56c3 Allow nova to shoot Underwater 2025-08-06 14:22:33 -05:00
ShookEagle
8f556329cb Stop caching Zero'd sky 2025-08-05 17:32:20 -05:00
ShookEagle
ee12f527eb Don't Delete Sky 2025-08-05 17:04:55 -05:00
ShookEagle
5d42dacf43 Fix Ghost War Day 2025-08-05 16:50:53 -05:00
ShookEagle
e7e799abb3 Rj nerf and fixes 2025-08-05 13:16:54 -05:00
MSWS
b2f2411b1b More dev stuff 2025-08-05 08:52:11 -07:00
blankdvth
6844272ba2 fix: temporarily disable @aim for css_st due to CS# bug 2025-08-04 21:10:25 -04:00
ShookEagle
144f879562 i forgor to add Ghost :/ 2025-08-04 18:24:46 -05:00
Isaac
0d0453cfe6 Merge branch 'main' into dev 2025-08-04 16:05:55 -07:00
MSWS
66d5d245de Merge branch 'dev' of github.com:edgegamers/Jailbreak into dev 2025-08-04 15:39:13 -07:00
MSWS
df599050f2 Disable LR damage manager, mute on death 2025-08-04 15:38:45 -07:00
ShookEagle
96bc068494 Temporarily hard-code player eye level 2025-08-04 17:28:24 -05:00
ShookEagle
39c6ad6270 buff rocket damage 2025-08-04 17:24:58 -05:00
Isaac
8f942383c6 Revert "Revert HNS to CS:GO Format" (#390)
This reverts commit 5972950f78.
2025-08-04 01:58:07 -07:00
MSWS
7dd822b6de Revert "Revert HNS to CS:GO Format"
This reverts commit 5972950f78.
2025-08-04 01:56:31 -07:00
Isaac
33c4389fcf New SDs and feature fixes (#389) 2025-08-04 01:50:56 -07:00
MSWS
587d64fcf5 Remove debugs 2025-08-04 01:41:07 -07:00
MSWS
4b412dcc7b Update raytrace sigs 2025-08-04 01:04:40 -07:00
MSWS
b5603fafbc Fix C4 behavior 2025-08-04 00:21:15 -07:00
blankdvth
4df6446b18 chore: bump CSS from 1.0.327 to 1.0.329 2025-08-04 00:48:13 -04:00
blankdvth
8851e27004 Merge branch 'main' into dev 2025-08-03 23:30:29 -04:00
ShookEagle
ece5e9bb35 use HookMode.Pre used to prevent dangling handler 2025-07-27 11:40:06 -05:00
ShookEagle
19f240d918 update sounds 2025-07-27 10:36:37 -05:00
ShookEagle
d7f1149317 Cleanup Day 2025-07-27 10:31:38 -05:00
ShookEagle
bda2fd7a43 Don't strip knives 2025-07-27 10:24:32 -05:00
ShookEagle
fc8b7f0cff override to avoid double messaging 2025-07-27 10:23:53 -05:00
ShookEagle
75094be9cf Switch to VirtualFunc for proper damage handling 2025-07-27 10:21:33 -05:00
ShookEagle
22e9e45811 Fix Starting Fog Distance 2025-07-27 09:52:11 -05:00
ShookEagle
6a804dd2a5 Fix Messaging 2025-07-27 09:50:47 -05:00
ShookEagle
9e69f774ce fix convars 2025-07-26 21:32:02 -05:00
ShookEagle
963bb85c28 Fix End Round Crashes 2025-07-26 12:04:33 -05:00
Reece
cccf2c41e7 Feat: Rocket Jump Day (#388)
Creates a Special Day with Rocket Jump like Market Gardener from TF2

[Example Video](https://streamable.com/tu6rif)
2025-07-26 10:29:34 -05:00
ShookEagle
a4a50e6a6d rename Into() => ToVec3() 2025-07-26 10:17:13 -05:00
ShookEagle
31ada4a226 cleanup sets 2025-07-26 10:16:04 -05:00
ShookEagle
310606f37f Add Documentation 2025-07-26 10:15:38 -05:00
ShookEagle
f0b96663bc renaming 2025-07-26 10:06:06 -05:00
ShookEagle
5a98918f71 Removed obsolete shots Dictionary 2025-07-26 10:04:46 -05:00
ShookEagle
8f370df230 RJ > RocketJump 2025-07-26 10:01:57 -05:00
ShookEagle
97381652ec Add Rocket Jump Special Day 2025-07-25 10:43:02 -05:00
ShookEagle
f010bce92c Add Extensions 2025-07-25 10:41:17 -05:00
ShookEagle
d8996efb41 Merge remote-tracking branch 'origin/dev' into dev 2025-07-19 12:17:06 -05:00
ShookEagle
11b527d9fc Trigger Build 2025-07-19 12:16:33 -05:00
Reece
888906fc3a Special Days Revamp (#387)
### Changelog:
- Fix: failing weapon restrictions
- Fix: infection day not maintaining the ct : t ratio
- Fix: infection day queuing people who weren't previously queued for ct
- Fix: infection day not keeping initial ct team
- Change: Hide and Seek to Old CS:GO Format
- Feat: `HE Only` Special Day
- Feat: `Ghost War` Special Day
- Feat New `Fog War` Special Day
  * FFA mode where players are surrounded by fog that grows slowly after a
certain time
2025-07-19 12:12:01 -05:00
ShookEagle
c9083a3630 Cleanup 2025-07-19 08:10:56 -05:00
ShookEagle
e75baccdd0 Unregister Listeners and Apply Formating 2025-07-19 07:44:23 -05:00
ShookEagle
ef74889949 Feat: New Fog War Special Day 2025-07-18 14:07:46 -05:00
ShookEagle
ce27b3ad45 Re Add InfectionDay 2025-07-18 12:28:03 -05:00
ShookEagle
169f9883ae Add GhostWar Alias 2025-07-18 12:26:52 -05:00
ShookEagle
5ce074f0b1 Add HE command alias 2025-07-18 12:26:16 -05:00
ShookEagle
274319e95f Add Ghost War 2025-07-14 17:59:48 -05:00
ShookEagle
dc49770716 Add message provider lol 2025-07-14 16:47:27 -05:00
ShookEagle
5972950f78 Revert HNS to CS:GO Format 2025-07-14 16:03:12 -05:00
ShookEagle
aad9263123 Revert "Open Cells on SpeedRunners"
This reverts commit 3d9283993e.
2025-07-14 15:28:05 -05:00
ShookEagle
43926f8ec4 Update BasePlugin captures 2025-07-14 15:26:48 -05:00
ShookEagle
4fb8e5c599 Add HE Only Day 2025-07-14 15:25:35 -05:00
ShookEagle
74505b647d Switch round-end listener to HookMode.Pre 2025-07-14 14:55:48 -05:00
ShookEagle
9cf357a522 Upgrade cs# to fix failing weapon restrctions due to invalid CCSPlayer_ItemServices_CanAcquireFunc signature 2025-07-14 12:23:26 -05:00
ShookEagle
3d9283993e Open Cells on SpeedRunners 2025-07-14 12:06:51 -05:00
Isaac
bec433f05c ci: Add weekly trigger to Github Workflow (#386) 2025-06-30 02:46:51 -07:00
MSWS
6961fa9c81 ci: Add weekly trigger to Github Workflow 2025-06-30 02:45:56 -07:00
MSWS
537744712d Revert "ci: Add weekly trigger to Github Workflow"
This reverts commit 34fcf653c8.
2025-06-30 02:42:00 -07:00
MSWS
37f6cccecf erge branch 'dev' into feat/autowarden 2025-06-30 02:41:46 -07:00
MSWS
34fcf653c8 ci: Add weekly trigger to Github Workflow 2025-06-30 02:40:15 -07:00
blankdvth
da31614dec Trigger Build 2025-06-27 03:42:11 -04:00
blankdvth
a24963cb17 Trigger Build 2025-06-24 16:15:28 -04:00
ShookEagle
d2e048f3d8 Update to player.HasMovedSinceSpawn schema :kek: 2025-06-05 15:17:44 -05:00
ShookEagle
51f87a9c08 Clear Spawns On Round Start 2025-06-05 14:51:54 -05:00
ShookEagle
9067a09b03 Trigger Build 2025-06-05 14:44:37 -05:00
ShookEagle
921613ab65 Give Tolerance On Player Spawn loacation 2025-06-05 12:51:30 -05:00
ShookEagle
7ec893ddda test When Poststart gets called 2025-06-05 12:40:32 -05:00
ShookEagle
3406fff706 Try post-start 2025-06-05 12:33:47 -05:00
ShookEagle
d2c945bf43 check Pawn origin lol 2025-06-04 22:12:52 -05:00
blankdvth
7e5e4bca29 Trigger Build 2025-06-04 22:54:25 -04:00
blankdvth
fd43b750a7 Trigger Build 2025-06-04 22:54:12 -04:00
Reece
064f0eb51a Fix: Auto Warden Msg / AFK handing (#384) 2025-05-26 16:39:33 -05:00
ShookEagle
8e8a22194e give players time to "spawn" 2025-05-26 16:38:18 -05:00
ShookEagle
2a062b0a7e avoid silently adding players 2025-05-26 16:36:56 -05:00
Reece
3f1d50d584 Auto Warden DS Command (#383) 2025-05-24 18:21:57 -05:00
ShookEagle
9ce8893aa0 check movement after 5s 2025-05-24 13:18:44 -05:00
ShookEagle
c37c2d3b0d Check Movement before queueing for warden 2025-05-24 08:39:25 -05:00
blankdvth
2952e98bfa Trigger Build 2025-05-23 02:14:22 -04:00
blankdvth
c9fc1f444c Trigger Build 2025-05-23 02:14:08 -04:00
ShookEagle
3d677fb679 Auto Warden Impl 2025-05-22 17:22:55 -05:00
ShookEagle
0e591ebe79 Auto Warden Locale 2025-05-22 17:22:47 -05:00
Isaac
12b58160c1 Dev (#382) 2025-05-16 12:50:29 -07:00
MSWS
7650556948 fix: Update Guard health reference in GunToss LastRequest
Update `Guard.SetHealth` to reference `Guard.PlayerPawn.Value!.Health` instead of `Guard.Health` in GunToss.cs
2025-05-14 23:50:55 -07:00
Kaleb Schmenk
6cf980bd14 Countdown bad parameter message 2025-05-15 02:39:34 -04:00
Kaleb Schmenk
e06b8c4623 Countdown Cleaner timer implementation 2025-05-15 02:15:50 -04:00
Kaleb Schmenk
ec6606f800 Fixed countdown bugs 2025-05-15 02:07:37 -04:00
Isaac
f072f392fe Rebel and LR Balancing (#381)
- Restricted !rebel to only be usable as the last prisoner alive
- Re-added additional HP to CTs within gun toss LRs
2025-05-14 22:28:24 -07:00
Kaleb Schmenk
9bd1668e06 Comment out countdown audio 2025-05-15 00:32:57 -04:00
MSWS
c58e9a033e feat: Update Gun Toss balance and rebel command restrictions
- Update Gun Toss logic in `GunToss.cs`:
- Adjust setup phase to set guard's health and armor to 500.
- Add checks ensuring only the last alive terrorist can use the command.
2025-05-14 21:28:00 -07:00
Kaleb Schmenk
b34d984ffb Update CountdownCommandBehavior.cs 2025-05-12 00:11:35 -04:00
Kaleb Schmenk
12f8fa7819 Update CountdownCommandBehavior.cs 2025-05-12 00:05:20 -04:00
Kaleb Schmenk
3510375c4a Update CountdownCommandBehavior.cs 2025-05-11 23:58:15 -04:00
Kaleb Schmenk
7688fa67d9 Update CountdownCommandBehavior.cs 2025-05-11 23:28:22 -04:00
Kaleb Schmenk
cc7b21286a Update CountdownCommandBehavior.cs 2025-05-11 23:12:28 -04:00
Kaleb Schmenk
7b8a6c514b Update CountdownCommandBehavior.cs 2025-05-11 23:05:34 -04:00
Kaleb Schmenk
f9be61e62d Update CountdownCommandBehavior.cs 2025-05-11 22:56:01 -04:00
Kaleb Schmenk
9ef36aa97e Countdown New Sound Implementation 2025-05-11 22:49:08 -04:00
Kaleb Schmenk
4482291c44 Update CountdownCommandBehavior.cs 2025-05-11 20:15:57 -04:00
Kaleb Schmenk
4413de9933 Test Countdown Sounds 2025-05-11 20:10:11 -04:00
Kaleb Schmenk
fe42cd41dd Update CountdownCommandBehavior.cs 2025-05-11 12:27:48 -04:00
Kaleb Schmenk
2744ca54c0 Fix Admin Force Countdown 2025-05-11 12:17:54 -04:00
Kaleb Schmenk
0936f57f2a Admin Force Countdown Fix 2025-05-11 12:15:22 -04:00
Kaleb Schmenk
c155e3e720 Countdown Minimum Duration 2025-05-11 12:12:15 -04:00
Kaleb Schmenk
c70cad0614 Countdown comments 2025-05-11 00:05:44 -04:00
Kaleb Schmenk
f029049302 Update CountdownCommandBehavior.cs 2025-05-10 23:54:18 -04:00
Kaleb Schmenk
1938ba65ab Countdown GOGOGO Fix (Again) 2025-05-10 23:46:10 -04:00
Kaleb Schmenk
80f1cf8802 Countdown GOGOGO Fix 2025-05-10 23:42:56 -04:00
Kaleb Schmenk
3aad5eba3c Countdown Lambda Capture Fix 2025-05-10 23:40:31 -04:00
Kaleb Schmenk
2b47b10afb Update CountdownCommandBehavior.cs 2025-05-10 23:30:27 -04:00
Kaleb Schmenk
95bd6a0ff2 Countdown Command Feature Testing 2025-05-10 23:22:30 -04:00
Kaleb Schmenk
17acc5f174 Suiciding as a rebel results in a credit reward FIX 2025-05-10 21:31:47 -04:00
Kaleb Schmenk
1cddf43798 Allow Generic Admins Debug Commands
Temporary debugging measure
2025-05-10 21:03:03 -04:00
Isaac
00ab901022 Give knifes to winner of bullet for bullet LRs (#380) 2025-05-06 16:50:46 -07:00
MSWS
4bf9924ddb Merge branch 'dev' of github.com:edgegamers/Jailbreak into dev 2025-05-06 16:48:53 -07:00
MSWS
294d761cf8 feat: Give knife to winner of bullet for bullet LRs
- Modify `OnEnd` logic to allocate knife items based on specific result conditions using a switch statement.
2025-05-06 16:48:10 -07:00
Kaleb Schmenk
cc206db226 Infection CT Ratio Debugging
Instead of caching player slots we cache the player themselves. This way we do not have possible off by 1 or more errors when players leave.
2025-05-05 16:12:49 -04:00
32 changed files with 1291 additions and 278 deletions

View File

@@ -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

Binary file not shown.

View 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")
};
}
}

View File

@@ -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."
};
}
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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,

View File

@@ -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);
}

View File

@@ -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 {

View File

@@ -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) {

View File

@@ -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;

View File

@@ -4,6 +4,7 @@
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>

View File

@@ -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()
};
}

View 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;
}
}
}

View 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 dont! 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;
}
}
}

View 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"};
}
}
}

View File

@@ -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;
}

View 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; }
}
}

View 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;
}
}

View File

@@ -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 })

View File

@@ -31,6 +31,7 @@
<ItemGroup>
<PackageReference Include="CS2ScreenMenuAPI" Version="2.6.0" />
<PackageReference Include="CS2TraceRay" Version="1.0.8" />
</ItemGroup>
</Project>

View File

@@ -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++) {

View File

@@ -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;
}

View File

@@ -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();

View 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);
});
}
}

View File

@@ -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>();

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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"/>

View File

@@ -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;
}

View File

@@ -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
};
}