272 Commits

Author SHA1 Message Date
Isaac
6900848ef9 Re-add removed features (#401) 2025-10-12 11:44:58 -07:00
MSWS
b8beac3d14 Re-apply prod fixes 2025-10-12 11:42:59 -07:00
MSWS
4bde4e8f0a Re-add features 2025-10-12 11:31:10 -07:00
MSWS
1cf95f1b3d Remove TODO comment 2025-10-12 10:21:22 -07:00
MSWS
2182c22e44 Re-add removed features 2025-10-12 10:20:37 -07:00
Isaac
a2dd074403 Simplify LR Manager / Guntoss (#400) 2025-10-09 10:02:29 -07:00
MSWS
faeec955f8 Merge branch 'dev' of github.com:edgegamers/Jailbreak into dev 2025-10-09 10:00:36 -07:00
MSWS
4e94f724e9 refactor: Refactor damage and event handling logic for LRs
- Remove obsolete method overload in `IDamageBlocker.cs` to simplify the interface.
- Simplify logic in `GunToss.cs` by removing initialization and cleanup operations, locale injection, and timer setup for guard status.
- Refactor `LastRequestManager.cs` by:
  - Adding `JetBrains.Annotations` and replacing game event handler attributes for better annotation usage.
  - Streamlining last request event handling by removing redundant tasks and encapsulating utility functions.
  - Suppressing extra damage event broadcasts and improving message clarity.
  - Adjusting round time management logic for last requests.
2025-10-09 09:59:54 -07:00
Isaac
b89f2c1b60 Fix: Paint and Misc (#399)
Changelogs:
- Fix Paint
- Temporarily disable RJ until further notice
- Speculative LR Crash Fixes
- Speculative LR Damage Bug Fixes
2025-09-29 20:43:08 -07:00
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
Isaac
54e9262b56 Autowarden DS Perk, Special Day Tweaks (#396)
## Changelogs:
- 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`
- Special Days
  - Rocket Jump
    - Delay Nova shots underwater
    - Buff Damage from 57 -> 65
    - Fix typos in messaging 
  - Ghost War
    - Fix Invisibility
2025-08-11 00:31:13 -07: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
Reece
9a5acc5926 Dev (#394) 2025-08-05 17:44:52 -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
blank_dvth
9cba0c155d fix: temporarily disable @aim for css_st due to CS# bug (#393) 2025-08-04 21:26:20 -04:00
blankdvth
6844272ba2 fix: temporarily disable @aim for css_st due to CS# bug 2025-08-04 21:10:25 -04:00
Reece
dcc4da4ac5 Add Ghost War Day (#392) 2025-08-04 18:32:26 -05:00
ShookEagle
144f879562 i forgor to add Ghost :/ 2025-08-04 18:24:46 -05:00
Isaac
6ec15da3cf Dev (#391) 2025-08-04 16:07:57 -07: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
Isaac
51365a3526 Gameplay Tweaks (#379)
- Reduced time given for Last Guard
- Removed Infection day
- Replaced "Spawn Red" RTD reward with "Cannot use rifles"
- Fixed a bug where the wrong player got the negev on Gun Game
- Tweaked elimination numbers for Speedrun day to allow it to last
slightly longer
- Removed DS restrictions for all special days except for HNS and
Speedrun, which are now Silver only
2025-04-26 02:27:00 +00:00
MSWS
8ce1556711 feat: Normalize input and add HNS to SDType logic
Update `SDType` methods in `Enums` for improved input handling and new type support

- Update `FromString` method to normalize input by converting to lowercase and removing spaces
- Add support for `HNS` and `SPEEDRUN` types in the `CanCall` method logic

[public/Jailbreak.Public/Mod/SpecialDay/Enums/SDType.cs]
Updated the `FromString` method to normalize input by converting it to lowercase and removing spaces before matching with specific conditions.
Added support for `HNS` type in the `CanCall` method logic along with `SPEEDRUN` type.
2025-04-25 19:20:07 -07:00
MSWS
1845621483 feat: Adjust gameplay mechanics and round settings.
```
- Adjust player elimination thresholds for SpeedrunDay in `SpecialDays/SpeedrunDay.cs`.
- Update round timings for Last Guard in `LastGuard.cs`.
- Disable creation of InfectionDay in `SpecialDayFactory.cs`.
- Refine reward handling and restrictions in `RewardGenerator.cs`.
- Overhaul weapon progression, cleanup logic, and gameplay mechanics for GunGameDay in `SpecialDays/GunGameDay.cs`.
- Improve SDType parsing, adjust supporter permission checks, and temporarily disable Gold Supporter permission for SPEEDRUN in `Enums/SDType.cs`.
```

[mod/Jailbreak.SpecialDay/SpecialDays/SpeedrunDay.cs]
- Adjusted the player elimination thresholds for various player counts, reducing the number of eliminations for each range.
[mod/Jailbreak.LastGuard/LastGuard.cs]
- Changed the base round time for the Last Guard from 60 to 30 seconds.
- Adjusted the maximum round time for the Last Guard from 120 to 90 seconds.
[mod/Jailbreak.SpecialDay/SpecialDayFactory.cs]
A line initializing `SDType.INFECTION` was commented out, effectively disabling the creation of `InfectionDay`.
[mod/Jailbreak.RTD/RewardGenerator.cs]
- Commented out the addition of a new color reward with red color.
- Added a new reward restriction for rifles under the "Very low" probability category.
[public/Jailbreak.Public/Mod/SpecialDay/Enums/SDType.cs]
Modified the `FromString` method to handle additional `type` string variations with a case-insensitive replacement and matching for SDType values.

Removed `OITC` and `NOSCOPE` from the conditions checking Silver Supporter permissions in the `CanCall` method and instead focused on `SPEEDRUN`.

Commented out the Gold Supporter permission check and message for `SPEEDRUN` in the `CanCall` method, making it currently inactive.
[mod/Jailbreak.SpecialDay/SpecialDays/GunGameDay.cs]
- Adjusted the weapon progression logic by introducing additional shuffles for weapon categories and modifying order initialization.
- Introduced unhooking of the `CCSPlayer_ItemServices_CanAcquireFunc` virtual function when a player wins the game.
- Added a condition to verify valid acquisition of weapons based on progression and weapon categories, overriding the default behavior when necessary.
- Enhanced end-round cleanup by deregistering event handlers for player death and spawn.
- Minor adjustments to gameplay mechanics, including changes to player speed, weapon handling, and damage disabling for the winning player.
- Updated round settings and server commands to adjust behavior at critical game stages.
2025-04-25 19:13:46 -07:00
blank_dvth
b4df6302b6 Add fallback loading for the AutoRTD cookie (#378) 2025-04-23 21:01:38 -04:00
blankdvth
37adbf1853 fix(autortd): attempt to load cookie on map start if it fails to on plugin start 2025-04-23 20:46:19 -04:00
blankdvth
8bed3fd086 Trigger Build 2025-04-21 17:32:49 -04:00
blankdvth
5d6cef55de Trigger Build 2025-04-21 17:01:46 -04:00
Kaleb Schmenk
b95c386eb9 Revert Debugging Code For AutoRTD
Removed the debugging code for AutoRTD bug
2025-04-12 23:29:53 -04:00
blankdvth
1c9603e276 Trigger Build 2025-04-09 02:08:33 -04:00
Isaac
7cf0e194be Sanity check debugging (#377)
Verbose value setting and debug print outs
2025-04-07 18:17:27 -07:00
Kaleb Schmenk
65101d7490 Sanity check debugging
Verbose value setting and debug print outs
2025-04-07 17:01:57 -04:00
Isaac
58185c09ad Null Cookie Retry (#376)
Tries to get cookie again if it failed on start. Mainly for debugging
purposes, only seems to be happening on dev server.
2025-04-05 19:41:01 -07:00
Kaleb Schmenk
a5a5809a34 Remove not needed using 2025-04-05 19:52:33 -04:00
Kaleb Schmenk
8e2e8ea73d Remove unreachable code 2025-04-05 19:41:21 -04:00
Kaleb Schmenk
d24f6d8b47 Null cookie retry 2025-04-05 19:37:52 -04:00
Kaleb Schmenk
e3b34685ec Revert "Comment out permission checks"
This reverts commit 2403ace85b.
2025-04-05 19:30:35 -04:00
Kaleb Schmenk
2403ace85b Comment out permission checks 2025-04-05 19:27:31 -04:00
Isaac
0d9e830e11 Remove Permission Check For Debugging (#375)
Temporarily remove permission checking for Auto RTD to allow for
debugging
2025-04-05 14:48:21 -07:00
Kaleb Schmenk
dcdd504d05 Remove Permission Check For Debugging 2025-04-05 17:46:33 -04:00
Isaac
472ccf01b8 Debugging Messages For AutoRTD (#374)
Added some debugging messages to chat so that I can view the state of
the variable values at runtime
2025-04-05 14:30:12 -07:00
Kaleb Schmenk
a28eb746a8 Debugging Messages For AutoRTD
Added some debugging messages to chat so that I can view the state of the variable values at runtime
2025-04-05 15:34:02 -04:00
blankdvth
0041d36bad Trigger Build 2025-04-04 22:20:12 -04:00
MSWS
0682427979 Force CI test 2025-03-31 18:20:47 -07:00
MSWS
b7cf0af6c1 Run Job 2025-03-27 19:50:38 -07:00
Isaac
3f307dcd03 Fix S4S/M4M LR (Switch EventBulletImpact over to EventWeaponFire) (#373)
https://gitlab.edgegamers.io/source2/cs2/servers/jailbreak/-/issues/136
2025-03-15 21:00:41 -07:00
blankdvth
5223db8e39 Switch EventBulletImpact over to EventWeaponFire 2025-03-12 21:40:25 -04:00
MSWS
c49580800d Merge branch 'dev' 2025-02-26 15:43:51 -08:00
MSWS
4d5ad8dca3 Remove right-click for markers 2025-02-26 15:43:42 -08:00
Isaac
97faee0977 Dev (#372) 2025-02-25 20:11:30 -08:00
MSWS
38d0a0f8ab Switch back to timer 2025-02-25 20:10:59 -08:00
MSWS
e7b8b5c28c Updates 2025-02-25 19:12:26 -08:00
Isaac
45a71a9755 Reduce rate limit and marker resolution (#371) 2025-02-25 18:16:55 -08:00
MSWS
3666814699 Reduce rate limit and marker resolution 2025-02-25 18:16:31 -08:00
Isaac
c8403dec59 Fix marker removing (#370) 2025-02-25 02:56:14 -08:00
MSWS
3b80d551ca Only register binds on spawn 2025-02-25 02:56:06 -08:00
MSWS
1b032b2506 Fix marker removing 2025-02-25 02:55:08 -08:00
Isaac
132c94b058 Dev (#369) 2025-02-25 02:36:27 -08:00
MSWS
ba4eccd49e Reduce LG HP 2025-02-25 02:30:53 -08:00
MSWS
21a22acd1f Fix \!rebel HP low max cap 2025-02-25 02:30:19 -08:00
MSWS
6c2ab5e89c Disable cell snitching 2025-02-25 02:28:39 -08:00
MSWS
ddd4402ac5 Final touches 2025-02-25 02:27:30 -08:00
MSWS
cdeb71f48e Merge feat/raytrace 2025-02-25 02:17:20 -08:00
Isaac
5b6b6fe011 Dev (#368) 2025-02-22 18:47:32 -08:00
MSWS
70e8efd2d8 Merge branch 'dev' of github.com:edgegamers/Jailbreak into dev 2025-02-22 18:45:57 -08:00
MSWS
f34e6df4d3 Modify LR time and RTD odds 2025-02-22 18:29:45 -08:00
blank_dvth
5450024a86 Properly log player deaths (#367)
Currently player deaths/kills are not properly logged, this
(theoretically) fixes that -- needs to be tested on dev.
2025-02-19 00:27:55 -05:00
blankdvth
7fc91cc95c remove unnecessary null check 2025-02-18 23:36:52 -05:00
blankdvth
9351c38efb fix(logs): properly log player kills/deaths 2025-02-18 22:22:48 -05:00
Isaac
a428bd6886 Add message indicating command stays on pass (#365)
To prevent confusion among Ts, this PR returns the message on pass/fire
indicating that the command stays and when it becomes a freeday.

~~This PR does *not* implement a timer of any form to actually declare
it a freeday after those 10s, though that *may* be desirable behaviour?
(I can't remember whether this was the case in GO). If so, let me
know.~~
2025-02-18 11:23:17 -08:00
Isaac
a3a4da7307 Update JB to latest gangs (#366) 2025-02-18 11:19:24 -08:00
MSWS
5fb5b31c87 Update JB to latest gangs 2025-02-18 11:17:07 -08:00
blankdvth
0b091692cb Embed command stands into actual pass/fire messages, announce freeday in 10s 2025-02-18 00:47:32 -05:00
blankdvth
45976f0232 Add message indicating command stays on pass 2025-02-17 20:25:29 -05:00
Isaac
9ec911addf Reduce RTD delay again (#364) 2025-02-15 11:45:13 -08:00
MSWS
9c15b87400 Reduce RTD delay again 2025-02-15 11:42:03 -08:00
Isaac
73d01ea772 Fix LR crashes (#363) 2025-02-13 09:50:28 -08:00
MSWS
09d5cd9129 Fix LR crashes 2025-02-13 09:47:59 -08:00
Isaac
1afa53718a Delay RTD rewards more to support ghost (#362) 2025-02-10 11:05:10 -08:00
MSWS
6cc534e3a4 Delay RTD rewards more to support ghost 2025-02-10 11:01:39 -08:00
Isaac
49e5dd4c81 Allow CTs to pickup knives (#361) 2025-02-09 22:06:36 -08:00
MSWS
d9152a5870 Allow CTs to pickup knives 2025-02-09 20:24:53 -08:00
Isaac
3f2e334b44 Add valid checks to gun toss (#360) 2025-02-08 14:12:31 -08:00
MSWS
cdaad4542a Add valid checks to gun toss 2025-02-08 14:06:12 -08:00
Isaac
3b2f6d6961 Disable race lr (#359) 2025-01-24 18:12:18 -08:00
MSWS
eaeb490797 Disable race lr 2025-01-24 17:48:40 -08:00
Isaac
41646ec179 Add disconnect check for race (#358) 2025-01-23 23:00:35 -08:00
MSWS
326d1b7300 Add disconnect check for race 2025-01-23 20:21:29 -08:00
Isaac
8ba575ed3a Dev (#357) 2025-01-21 17:05:01 -08:00
MSWS
590fbdf3fb Update warden death message 2025-01-21 16:58:08 -08:00
MSWS
8a76c0824e Remove extra guard HP in gun toss LR 2025-01-21 10:52:25 -08:00
MSWS
a1bc5dd5ae Force higher HP 2024-12-29 23:47:30 -08:00
MSWS
65bf3eafd6 Fix SDColor not applying 2024-12-29 17:01:10 -08:00
MSWS
81064bf9bf Populate roundStart Timestamp 2024-12-29 10:41:09 -08:00
MSWS
e249eba1ce Re-add LR check 2024-12-29 10:35:00 -08:00
MSWS
6c8619f073 Switch back to ontick 2024-12-29 10:18:17 -08:00
MSWS
c571f925e0 Disable LR check 2024-12-29 10:07:14 -08:00
MSWS
c7ba9c0300 Merge branch 'main' of github.com:edgegamers/Jailbreak 2024-12-28 23:42:45 -08:00
MSWS
9a8047f79a Fix crashing 2024-12-28 23:41:22 -08:00
MSWS
9ebbdf2c6d Use concurrent 2024-12-28 22:50:43 -08:00
Isaac
fb12732dc9 Fix sd command feedback (#355) 2024-12-28 22:33:14 -08:00
MSWS
ba24da2350 Fix sd command feedback 2024-12-28 22:26:39 -08:00
MSWS
e9324127df Actually fix recursive dependency 2024-12-28 21:39:51 -08:00
MSWS
ae444a7fce Merge branch 'main' of github.com:edgegamers/Jailbreak 2024-12-28 21:22:38 -08:00
MSWS
90163b1778 Fix recursive dependency 2024-12-28 21:22:29 -08:00
Isaac
79819ce617 Fix LR damage (#354) 2024-12-28 21:07:55 -08:00
MSWS
ec904a5788 Add dont broadcast 2024-12-28 21:04:28 -08:00
MSWS
7a70740fcb Merge branch 'dev' of github.com:edgegamers/Jailbreak into dev 2024-12-28 21:03:08 -08:00
MSWS
8ea19a62df Dont damage players in LR with bomb 2024-12-28 21:03:00 -08:00
Isaac
d0ec40f7aa Rebase dev (#353) 2024-12-28 20:03:48 -08:00
Isaac
19ee8c2e62 Dev (#352) 2024-12-28 19:53:26 -08:00
MSWS
d8a76d6162 Small cleanups 2024-12-28 16:19:11 -08:00
MSWS
9e16e50d98 Fire kill event to attacker on gungame 2024-12-28 16:15:05 -08:00
MSWS
b987887974 Fix speedrunners sometimes killing down to 1 2024-12-28 16:06:45 -08:00
MSWS
840f8f6af2 Enforce DS for some SDs 2024-12-28 15:18:28 -08:00
MSWS
1010a57b28 Dont explode bomb if it is a new round 2024-12-27 14:08:59 -08:00
MSWS
08359231b3 Switch to escaped newline 2024-12-27 14:03:12 -08:00
MSWS
ba9dcdbcc8 Fix Race LR 2024-12-27 13:27:32 -08:00
MSWS
d61a35a023 Formatting 2024-12-27 12:52:58 -08:00
Isaac
96aa9bfc9c Reformat and cleanup (#351) 2024-12-27 12:44:06 -08:00
MSWS
2a254b683b Reformat and cleanup 2024-12-27 12:37:12 -08:00
MSWS
e95d9f99c2 Fix localization 2024-12-27 03:56:10 -08:00
79 changed files with 1936 additions and 679 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

@@ -9,8 +9,8 @@ public class CoinflipLocale : LastRequestLocale, ILRCFLocale {
public IView FailedToChooseInTime(bool choice) {
return new SimpleView {
PREFIX,
"You failed to choose in time, defaulting to" + ChatColors.Green,
choice ? "Heads" : "Tails"
"You failed to choose in time, defaulting to",
$"{ChatColors.Green} {(choice ? "Heads" : "Tails")}{ChatColors.Grey}."
};
}
@@ -18,17 +18,16 @@ public class CoinflipLocale : LastRequestLocale, ILRCFLocale {
return new SimpleView {
PREFIX,
guard,
"chose" + ChatColors.Green,
choice ? "Heads" : "Tails",
ChatColors.Default + ", flipping..."
"chose",
$" {ChatColors.Green}{(choice ? "Heads" : "Tails")}{ChatColors.Grey}, flipping..."
};
}
public IView CoinLandsOn(bool heads) {
return new SimpleView {
PREFIX,
"The coin landed on" + ChatColors.Green,
heads ? "Heads" : "Tails" + ChatColors.White + "."
"The coin landed on",
$" {ChatColors.Green}{(heads ? "Heads" : "Tails")}{ChatColors.Grey}."
};
}
}

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

@@ -48,4 +48,10 @@ public class SDLocale : ISDLocale, ILanguage<Formatting.Languages.English> {
"second" + (maxTime == 1 ? "" : "s") + " of round start."
};
}
public IView CannotCallDay(string reason) {
return new SimpleView {
PREFIX, "You cannot call this special day:", ChatColors.Red + reason
};
}
}

View File

@@ -16,6 +16,9 @@ public class WardenLocale : IWardenLocale,
Plain = false, Panorama = false, Chat = true
};
public static readonly FormatObject COMMAND_STANDS =
new HiddenFormatObject($"The previous command stands for 10 seconds.");
public IView PickingShortly
=> new SimpleView {
PREFIX,
@@ -28,13 +31,25 @@ public class WardenLocale : IWardenLocale,
$"No one in queue. Next guard to {ChatColors.BlueGrey}!warden{ChatColors.Grey} will become warden."
};
public IView NowFreeday
=> new SimpleView {
PREFIX,
$"It is now a freeday! CTs must pursue {ChatColors.BlueGrey}!warden{ChatColors.Grey}."
};
public IView WardenLeft
=> new SimpleView { PREFIX, "The warden left the game." };
=> new SimpleView { PREFIX, "The warden left the game.", COMMAND_STANDS };
public IView WardenDied
=> new SimpleView {
PREFIX,
$"The warden {ChatColors.Red}died{ChatColors.Grey}. CTs must pursue {ChatColors.BlueGrey}!warden{ChatColors.Grey}."
{
PREFIX,
$"The warden {ChatColors.Red}died{ChatColors.Grey}. It is a freeday!"
},
SimpleView.NEWLINE, {
PREFIX,
$"CTs must pursue {ChatColors.BlueGrey}!warden{ChatColors.Grey}."
}
};
public IView BecomeNextWarden
@@ -63,13 +78,22 @@ 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 { PREFIX, player, "resigned from warden." };
return new SimpleView {
PREFIX, player, "resigned from warden.", COMMAND_STANDS
};
}
public IView FireWarden(CCSPlayerController player) {
return new SimpleView { PREFIX, player, "was fired from warden." };
return new SimpleView {
PREFIX, player, "was fired from warden.", COMMAND_STANDS
};
}
public IView
@@ -79,14 +103,15 @@ public class WardenLocale : IWardenLocale,
admin,
"fired",
player,
"from warden."
"from warden.",
COMMAND_STANDS
};
}
public IView NewWarden(CCSPlayerController player) {
return new SimpleView { PREFIX, player, "is now the warden." };
}
public IView CurrentWarden(CCSPlayerController? player) {
return player is not null ?
new SimpleView { PREFIX, "The warden is", player, "." } :
@@ -101,4 +126,25 @@ public class WardenLocale : IWardenLocale,
PREFIX, player, "was fired and is no longer the warden."
};
}
public IView MarkerPlaced(string marker) {
return new SimpleView {
PREFIX, $"{marker}{ChatColors.Grey} marker placed."
};
}
public IView MarkerRemoved(string marker) {
return new SimpleView {
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

@@ -1,4 +1,5 @@
using System.Runtime.CompilerServices;
using CounterStrikeSharp.API.Modules.Utils;
using GangsAPI;
using GangsAPI.Data;
using GangsAPI.Data.Command;
@@ -67,17 +68,11 @@ public abstract class AbstractEnumCommand<T>(IServiceProvider provider,
var gang = await gangs.GetGang(player.GangId.Value)
?? throw new GangNotFoundException(player.GangId.Value);
var (success, data) =
await gangStats.GetForGang<T>(player.GangId.Value, statId);
if (!success) data = def;
T equipped;
var (_, tmp) = await playerStats.GetForPlayer<T>(player.Steam, statId);
equipped = tmp;
var data = await gangStats.GetForGang<T>(player.GangId.Value, statId);
var tmp = await playerStats.GetForPlayer<T>(player.Steam, statId);
if (info.Args.Length == 1) {
openMenu(executor, data, equipped);
openMenu(executor, data, tmp);
return CommandResult.SUCCESS;
}
@@ -112,13 +107,15 @@ public abstract class AbstractEnumCommand<T>(IServiceProvider provider,
if (gangChat != null)
await gangChat.SendGangChat(player, gang,
localizer.Get(MSG.PERK_PURCHASED, $"{title} ({formatItem(val)})"));
localizer.Get(MSG.PERK_PURCHASED,
player.Name ?? player.Steam.ToString(),
$"{title} ({formatItem(val)})"));
return CommandResult.SUCCESS;
}
await playerStats.SetForPlayer(executor, statId, val);
executor.PrintToChat(
$"{localizer.Get(MSG.PREFIX)}Set your {title} to {formatItem(val)}.");
$"{localizer.Get(MSG.PREFIX)}Set your {ChatColors.BlueGrey}{title} to {ChatColors.LightBlue}{formatItem(val)}.");
return CommandResult.SUCCESS;
}

View File

@@ -48,7 +48,7 @@ public abstract class AbstractEnumMenu<T>(IServiceProvider provider, T data,
T item) {
if (item.Equals((T)(object)0))
return Task.FromResult(
$" {ChatColors.DarkBlue}Gang Perks: {ChatColors.LightBlue}{title}\n {ChatColors.Grey}{desc}");
$" {ChatColors.DarkBlue}Gang Perks: {ChatColors.LightBlue}{title}\\n {ChatColors.Grey}{desc}");
if (item.Equals(equipped))
return Task.FromResult(

View File

@@ -52,7 +52,7 @@ public class BasicPerkMenu(IServiceProvider provider, IPerk perk)
$" {ChatColors.DarkBlue}Gang Perk: {ChatColors.Blue}{perk.Name}";
var items = new List<string>();
if (perk.Description != null)
title += $"\n {ChatColors.LightBlue}{perk.Description}";
title += $"\\n {ChatColors.LightBlue}{perk.Description}";
items.Add(title);
if (cost != null) {
var color = await economy.CanAfford(player, cost.Value) ?

View File

@@ -1,6 +1,4 @@
using System.Net.Cache;
using CounterStrikeSharp.API.Modules.Utils;
using Microsoft.Extensions.Primitives;
namespace Gangs.BaseImpl.Stats;
@@ -22,7 +20,7 @@ public class LRData {
public override string ToString() {
return
$"{ChatColors.Blue}CT{ChatColors.Grey}/{ChatColors.Red}T {ChatColors.BlueGrey}LRs: {ChatColors.Blue}{CtLrs}{ChatColors.White}/{ChatColors.Red}{TLrs}\n"
$"{ChatColors.Blue}CT{ChatColors.Grey}/{ChatColors.Red}T {ChatColors.BlueGrey}LRs: {ChatColors.Blue}{CtLrs}{ChatColors.White}/{ChatColors.Red}{TLrs}\\n"
+ $"{ChatColors.Blue}CT{ChatColors.Grey}/{ChatColors.Red}T {ChatColors.BlueGrey}LR Wins: {ChatColors.Blue}{CTLrsWon}{ChatColors.White}/{ChatColors.Red}{TLrsWon}";
}
}

View File

@@ -1,5 +1,4 @@
using CounterStrikeSharp.API.Modules.Utils;
using Microsoft.Extensions.Primitives;
namespace Gangs.BaseImpl.Stats;
@@ -20,14 +19,14 @@ public class WardenData {
public override string ToString() {
var result =
$"{ChatColors.BlueGrey}Times Wardened: {ChatColors.Yellow}{TimesWardened}\n";
$"{ChatColors.BlueGrey}Times Wardened: {ChatColors.Yellow}{TimesWardened}\\n";
result +=
$"{ChatColors.BlueGrey}Warden {ChatColors.LightBlue}Deaths{ChatColors.BlueGrey}: {ChatColors.Yellow}{WardenDeaths}\n";
$"{ChatColors.BlueGrey}Warden {ChatColors.LightBlue}Deaths{ChatColors.BlueGrey}: {ChatColors.Yellow}{WardenDeaths}\\n";
result +=
$"{ChatColors.BlueGrey}Wardens {ChatColors.LightRed}Killed{ChatColors.BlueGrey}: {ChatColors.Yellow}{WardensKilled}\n";
$"{ChatColors.BlueGrey}Wardens {ChatColors.LightRed}Killed{ChatColors.BlueGrey}: {ChatColors.Yellow}{WardensKilled}\\n";
result +=
$"{ChatColors.BlueGrey}Guard {ChatColors.LightRed}Deaths{ChatColors.BlueGrey} as Warden: {ChatColors.Yellow}{GuardDeathsAsWarden}\n";
$"{ChatColors.BlueGrey}Guard {ChatColors.LightRed}Deaths{ChatColors.BlueGrey} as Warden: {ChatColors.Yellow}{GuardDeathsAsWarden}\\n";
result +=
$"{ChatColors.BlueGrey}Warden {ChatColors.LightRed}Deaths{ChatColors.BlueGrey} as Guard: {ChatColors.Yellow}{WardenDeathsAsGuard}";
return result;

View File

@@ -60,10 +60,8 @@ public class BombIconCommand(IServiceProvider provider) : ICommand {
var gang = await gangs.GetGang(player.GangId.Value)
?? throw new GangNotFoundException(player.GangId.Value);
var (success, data) =
await gangStats.GetForGang<BombPerkData>(gang, BombPerk.STAT_ID);
if (!success || data == null) data = new BombPerkData();
var data = await gangStats.GetForGang<BombPerkData>(gang, BombPerk.STAT_ID)
?? new BombPerkData();
if (info.ArgCount == 1) {
var menu = new BombIconMenu(provider, data);

View File

@@ -35,10 +35,9 @@ public class BombPerk(IServiceProvider provider)
public override async Task<IMenu?> GetMenu(IGangPlayer player) {
Debug.Assert(player.GangId != null, "player.GangId != null");
var (success, data) =
await gangStats.GetForGang<BombPerkData>(player.GangId.Value, STAT_ID);
if (!success || data == null) data = new BombPerkData();
var data =
await gangStats.GetForGang<BombPerkData>(player.GangId.Value, STAT_ID)
?? new BombPerkData();
return new BombIconMenu(Provider, data);
}

View File

@@ -26,7 +26,7 @@ public class GangsInit : IPluginBehavior {
_ = new BombIconBootstrap(services);
_ = new SDColorBootstrap(services);
_ = new CellsPerkBootstrap(services);
// _ = new CellsPerkBootstrap(services);
_ = new LRColorBootstrap(services);
_ = new WardenIconBootstrap(services);
_ = new SpecialIconBootstrap(services);

View File

@@ -31,11 +31,7 @@ public class CellsPerk(IServiceProvider provider) : BasePerk<int>(provider) {
public override async Task<int?> GetCost(IGangPlayer player) {
if (player.GangId == null || player.GangRank == null) return null;
var (success, cells) =
await gangStats.GetForGang<int>(player.GangId.Value, StatId);
if (!success) cells = 0;
var cells = await gangStats.GetForGang<int>(player.GangId.Value, StatId);
return getCostFor(cells + 1);
}
@@ -47,9 +43,7 @@ public class CellsPerk(IServiceProvider provider) : BasePerk<int>(provider) {
public override async Task OnPurchase(IGangPlayer player) {
if (player.GangId == null || player.GangRank == null) return;
var (success, cells) =
await gangStats.GetForGang<int>(player.GangId.Value, StatId);
if (!success) cells = 1;
var cells = await gangStats.GetForGang<int>(player.GangId.Value, StatId);
cells++;
await gangStats.SetForGang(player.GangId.Value, StatId, cells);

View File

@@ -13,7 +13,7 @@ public class LRColorPerk(IServiceProvider provider)
public const string STAT_ID = "jb_lr_color";
public const string DESC =
"Pick the color of you and your partner during your LRs\nConflicting colors are resolved by gang rank";
"Pick the color of you and your partner during your LRs\\nConflicting colors are resolved by gang rank";
private readonly IGangStatManager gangStats =
provider.GetRequiredService<IGangStatManager>();
@@ -38,10 +38,9 @@ public class LRColorPerk(IServiceProvider provider)
public override async Task<IMenu?> GetMenu(IGangPlayer player) {
Debug.Assert(player.GangId != null, "player.GangId != null");
var (success, data) =
var data =
await gangStats.GetForGang<LRColor>(player.GangId.Value, STAT_ID);
if (!success) data = LRColor.DEFAULT;
var (_, equipped) =
var equipped =
await playerStats.GetForPlayer<LRColor>(player.Steam, STAT_ID);
return new LRColorMenu(Provider, data, equipped);
}

View File

@@ -63,10 +63,9 @@ public class SDColorCommand(IServiceProvider provider) : ICommand {
var gang = await gangs.GetGang(player.GangId.Value)
?? throw new GangNotFoundException(player.GangId.Value);
var (success, data) =
await gangStats.GetForGang<SDColorData>(gang, SDColorPerk.STAT_ID);
if (!success || data == null) data = new SDColorData();
var data =
await gangStats.GetForGang<SDColorData>(gang, SDColorPerk.STAT_ID)
?? new SDColorData();
if (info.ArgCount == 1) {
var menu = new SDColorMenu(provider, data);
@@ -120,7 +119,7 @@ public class SDColorCommand(IServiceProvider provider) : ICommand {
}
data.Equipped = color;
await gangStats.SetForGang(gang, BombPerk.STAT_ID, data);
await gangStats.SetForGang(gang, SDColorPerk.STAT_ID, data);
if (gangChat == null) return CommandResult.SUCCESS;

View File

@@ -30,9 +30,9 @@ public class SDColorPerk(IServiceProvider provider)
public override async Task<IMenu?> GetMenu(IGangPlayer player) {
Debug.Assert(player.GangId != null, "player.GangId != null");
var (success, data) =
await gangStats.GetForGang<SDColorData>(player.GangId.Value, STAT_ID);
if (!success || data == null) data = new SDColorData();
var data =
await gangStats.GetForGang<SDColorData>(player.GangId.Value, STAT_ID)
?? new SDColorData();
return new SDColorMenu(Provider, data);
}

View File

@@ -36,10 +36,9 @@ public class SpecialIconPerk(IServiceProvider provider)
public override async Task<IMenu?> GetMenu(IGangPlayer player) {
Debug.Assert(player.GangId != null, "player.GangId != null");
var (success, data) =
var data =
await gangStats.GetForGang<SpecialIcon>(player.GangId.Value, STAT_ID);
if (!success) data = SpecialIcon.DEFAULT;
var (_, equipped) =
var equipped =
await playerStats.GetForPlayer<SpecialIcon>(player.Steam, STAT_ID);
return new SpecialIconMenu(Provider, data, equipped);
}

View File

@@ -13,6 +13,6 @@ public class WardenIconMenu(IServiceProvider provider, WardenIcon data,
}
override protected string formatItem(WardenIcon item) {
return $"{item.GetIcon()} ({item.ToString().ToTitleCase()}";
return $"{item.GetIcon()} ({item.ToString().ToTitleCase()})";
}
}

View File

@@ -38,10 +38,9 @@ public class WardenIconPerk(IServiceProvider provider)
public override async Task<IMenu?> GetMenu(IGangPlayer player) {
Debug.Assert(player.GangId != null, "player.GangId != null");
var (success, data) =
var data =
await gangStats.GetForGang<WardenIcon>(player.GangId.Value, STAT_ID);
if (!success) data = WardenIcon.DEFAULT;
var (_, equipped) =
var equipped =
await playerStats.GetForPlayer<WardenIcon>(player.Steam, STAT_ID);
return new WardenIconMenu(Provider, data, equipped);
}

View File

@@ -35,11 +35,10 @@ public class WardenPaintColorPerk(IServiceProvider provider)
public override async Task<IMenu?> GetMenu(IGangPlayer player) {
Debug.Assert(player.GangId != null, "player.GangId != null");
var (success, data) =
var data =
await gangStats.GetForGang<WardenPaintColor>(player.GangId.Value,
STAT_ID);
if (!success) data = WardenPaintColor.DEFAULT;
var (_, equipped) =
var equipped =
await playerStats.GetForPlayer<WardenPaintColor>(player.Steam, STAT_ID);
return new WardenPaintColorMenu(Provider, data, equipped);
}

View File

@@ -13,10 +13,11 @@ public class DebugZone(IServiceProvider services, BasePlugin plugin)
private readonly IDictionary<ulong, ITypedZoneCreator> creators =
new Dictionary<ulong, ITypedZoneCreator>();
private readonly IZoneFactory? factory = services.GetService<IZoneFactory>();
private readonly IZoneFactory factory =
services.GetRequiredService<IZoneFactory>();
private readonly IZoneManager? zoneManager =
services.GetService<IZoneManager>();
private readonly IZoneManager zoneManager =
services.GetRequiredService<IZoneManager>();
public override void OnCommand(CCSPlayerController? executor,
WrappedInfo info) {
@@ -27,11 +28,6 @@ public class DebugZone(IServiceProvider services, BasePlugin plugin)
return;
}
if (factory == null || zoneManager == null) {
info.ReplyToCommand("Zone factory or manager not found");
return;
}
var position = executor.PlayerPawn.Value.AbsOrigin;
if (info.ArgCount <= 1) {
@@ -90,17 +86,15 @@ public class DebugZone(IServiceProvider services, BasePlugin plugin)
}
if (zoneCount == 0) {
if (specifiedType.HasValue)
info.ReplyToCommand($"No {specifiedType} zones found");
else
info.ReplyToCommand("No zones found");
info.ReplyToCommand(specifiedType.HasValue ?
$"No {specifiedType} zones found" :
"No zones found");
return;
}
if (specifiedType.HasValue)
info.ReplyToCommand($"Showing {zoneCount} {specifiedType} zones");
else
info.ReplyToCommand($"Showing {zoneCount} zones");
info.ReplyToCommand(specifiedType.HasValue ?
$"Showing {zoneCount} {specifiedType} zones" :
$"Showing {zoneCount} zones");
return;
case "remove":
case "delete":
@@ -130,7 +124,6 @@ public class DebugZone(IServiceProvider services, BasePlugin plugin)
return;
case "reload":
case "refresh":
// Server.NextFrameAsync(async () => {
Task.Run(async () => {
await zoneManager.LoadZones(Server.MapName);
var count = (await zoneManager.GetAllZones()).SelectMany(e => e.Value)
@@ -186,11 +179,10 @@ public class DebugZone(IServiceProvider services, BasePlugin plugin)
.GetAwaiter()
.GetResult();
var toRemove = new List<IZone>();
foreach (var spawn in spawns)
if (doNotTeleport.Any(d
=> d.IsInsideZone(spawn.CalculateCenterPoint())))
toRemove.Add(spawn);
var toRemove = spawns.Where(spawn
=> doNotTeleport.Any(
d => d.IsInsideZone(spawn.CalculateCenterPoint())))
.ToList();
info.ReplyToCommand("Removing " + toRemove.Count
+ " auto-generated zones");

View File

@@ -30,12 +30,12 @@ public class LastGuard(ILGLocale notifications, ILastRequestManager lrManager,
"If true, the LG will be forced lower health if calculated");
public static readonly FakeConVar<double> CV_GUARD_HEALTH_RATIO = new(
"css_jb_lg_ct_hp_ratio", "Ratio of CT : T Health", 0.8,
"css_jb_lg_ct_hp_ratio", "Ratio of CT : T Health", 0.7,
ConVarFlags.FCVAR_NONE, new RangeValidator<double>(0.00001, 10));
public static readonly FakeConVar<int> CV_LG_BASE_ROUND_TIME =
new("css_jb_lg_time_base",
"Round time to set when LG is activated, 0 to disable", 60);
"Round time to set when LG is activated, 0 to disable", 30);
public static readonly FakeConVar<int> CV_LG_KILL_BONUS_TIME =
new("css_jb_lg_time_per_kill",
@@ -43,7 +43,7 @@ public class LastGuard(ILGLocale notifications, ILastRequestManager lrManager,
public static readonly FakeConVar<int> CV_LG_MAX_TIME =
new("css_jb_lg_time_max",
"Max round time to give the LG regardless of bonuses", 120);
"Max round time to give the LG regardless of bonuses", 90);
public static readonly FakeConVar<int> CV_LG_PER_PRISONER_TIME =
new("css_jb_lg_time_per_prisoner",
@@ -86,9 +86,9 @@ public class LastGuard(ILGLocale notifications, ILastRequestManager lrManager,
.ToList();
Task.Run(async () => {
foreach (var wrapper in players) {
var (success, stat) =
await gangStats.GetForPlayer<LGData>(wrapper, LGStat.STAT_ID);
if (!success || stat == null) stat = new LGData();
var stat =
await gangStats.GetForPlayer<LGData>(wrapper, LGStat.STAT_ID)
?? new LGData();
if (wrapper.Team == CsTeam.CounterTerrorist)
stat.CtLgs++;
else

View File

@@ -25,8 +25,8 @@ public class LastRequestFactory(ILastRequestManager manager,
LRType.ROCK_PAPER_SCISSORS => new RockPaperScissors(plugin, services,
prisoner, guard),
LRType.COINFLIP => new Coinflip(plugin, services, prisoner, guard),
LRType.RACE => new Race(plugin, manager, prisoner, guard,
services.GetRequiredService<ILRRaceLocale>()),
// LRType.RACE => new Race(plugin, manager, prisoner, guard,
// services.GetRequiredService<ILRRaceLocale>()),
LRType.MAG_FOR_MAG => new BulletForBullet(plugin, services, prisoner,
guard, true),
_ => throw new ArgumentException("Invalid last request type: " + type,

View File

@@ -28,6 +28,7 @@ using Jailbreak.Public.Mod.Rainbow;
using Jailbreak.Public.Mod.Rebel;
using Jailbreak.Public.Mod.Weapon;
using Jailbreak.Public.Utils;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using MStatsShared;
@@ -42,7 +43,7 @@ public class LastRequestManager(ILRLocale messages, IServiceProvider provider)
public static readonly FakeConVar<int> CV_LR_BONUS_TIME =
new("css_jb_lr_time_per_lr",
"Additional round time to add per LR completion", 30);
"Additional round time to add per LR completion", 20);
public static readonly FakeConVar<int> CV_LR_GUARD_TIME =
new("css_jb_lr_time_per_guard", "Additional round time to add per guard");
@@ -55,36 +56,40 @@ public class LastRequestManager(ILRLocale messages, IServiceProvider provider)
new("css_jb_min_players_for_credits",
"Minimum number of players to start giving credits out", 5);
public static readonly FakeConVar<int> CV_MAX_TIME_FOR_LR =
new("css_jb_max_time_for_lr", "Maximum round time during LR", 60);
private readonly IRainbowColorizer rainbowColorizer =
provider.GetRequiredService<IRainbowColorizer>();
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.Slot == attacker.Slot
|| victimLR.Guard.Slot == attacker.Slot)
// The person attacking is the victim's LR participant, allow damage
return false;
messages.DamageBlockedNotInSameLR.ToCenter(attacker);
@@ -153,9 +158,9 @@ public class LastRequestManager(ILRLocale messages, IServiceProvider provider)
var playerStatMgr = API.Gangs.Services.GetService<IPlayerStatManager>();
if (playerStatMgr != null)
Task.Run(async () => {
var (success, stat) =
await playerStatMgr.GetForPlayer<LRData>(wrapper, LRStat.STAT_ID);
if (stat == null || !success) stat = new LRData();
var stat =
await playerStatMgr.GetForPlayer<LRData>(wrapper, LRStat.STAT_ID)
?? new LRData();
if (wrapper.Team == CsTeam.Terrorist)
stat.TLrs++;
else
@@ -220,7 +225,8 @@ public class LastRequestManager(ILRLocale messages, IServiceProvider provider)
rainbowColorizer.StopRainbow(lr.Prisoner);
rainbowColorizer.StopRainbow(lr.Guard);
if (result is LRResult.GUARD_WIN or LRResult.PRISONER_WIN) {
RoundUtil.AddTimeRemaining(CV_LR_BONUS_TIME.Value);
// RoundUtil.AddTimeRemaining(CV_LR_BONUS_TIME.Value);
addRoundTimeCapped(CV_LR_BONUS_TIME.Value, CV_MAX_TIME_FOR_LR.Value);
messages.LastRequestDecided(lr, result).ToAllChat();
var wrapper =
@@ -297,46 +303,29 @@ public class LastRequestManager(ILRLocale messages, IServiceProvider provider)
if (playerStats == null || localizer == null || gangs == null
|| gangStats == null)
return;
var (aSuccess, aData) =
await playerStats.GetForPlayer<LRColor>(a, LRColorPerk.STAT_ID);
var (bSuccess, bData) =
await playerStats.GetForPlayer<LRColor>(b, LRColorPerk.STAT_ID);
if (!aSuccess) aData = LRColor.DEFAULT;
if (!bSuccess) bData = LRColor.DEFAULT;
var aData = await playerStats.GetForPlayer<LRColor>(a, LRColorPerk.STAT_ID);
var bData = await playerStats.GetForPlayer<LRColor>(b, LRColorPerk.STAT_ID);
LRColor? toApply = null;
PlayerWrapper? higher = null;
if (aSuccess && bSuccess) {
higher = await getHigherPlayer(a, b);
toApply = higher.Steam == a.Steam ? aData : bData;
} else if (aSuccess) {
toApply = aData;
higher = a;
} else if (bSuccess) {
toApply = bData;
higher = b;
}
if (toApply == null || higher == null) return;
higher = await getHigherPlayer(a, b);
if (toApply == null) return;
if (a.Player == null || b.Player == null) return;
var higherGang = await gangs.GetGang(higher.Steam);
if (higherGang == null) return;
var (gangSuccess, gData) =
var gData =
await gangStats.GetForGang<LRColor>(higherGang, LRColorPerk.STAT_ID);
if (!gangSuccess) return;
if ((gData & toApply.Value) == 0) return;
var color = toApply.Value.GetColor();
if (color == null) { // Player picked random, but we need to pick
// the random from their GANG's colors
var (success, gangData) =
var gangData =
await playerStats.GetForPlayer<LRColor>(higher, LRColorPerk.STAT_ID);
if (!success) return;
color = gangData.PickRandomColor();
}
@@ -443,9 +432,8 @@ public class LastRequestManager(ILRLocale messages, IServiceProvider provider)
private async Task<LRData?> getStat(PlayerWrapper player) {
var stats = API.Gangs?.Services.GetService<IPlayerStatManager>();
if (stats == null) return null;
var (success, data) =
await stats.GetForPlayer<LRData>(player, LRStat.STAT_ID);
if (!success || data == null) data = new LRData();
var data = await stats.GetForPlayer<LRData>(player, LRStat.STAT_ID)
?? new LRData();
return data;
}
@@ -455,14 +443,14 @@ public class LastRequestManager(ILRLocale messages, IServiceProvider provider)
}
private HookResult OnTakeDamage(DynamicHook hook) {
var info = hook.GetParam<CTakeDamageInfo>(1);
var playerPawn = hook.GetParam<CCSPlayerPawn>(0);
var info = hook.GetParam<CTakeDamageInfo>(1);
var player = playerPawn.Controller.Value?.As<CCSPlayerController>();
if (player == null || !player.IsValid) return HookResult.Continue;
var attackerPawn = info.Attacker;
var attacker = attackerPawn.Value?.As<CCSPlayerController>();
var attackerPawn = info.Attacker.Value?.As<CCSPlayerPawn>();
var attacker = attackerPawn?.As<CCSPlayerController>();
if (attacker == null || !attacker.IsValid) return HookResult.Continue;
@@ -471,11 +459,29 @@ public class LastRequestManager(ILRLocale messages, IServiceProvider provider)
HookResult.Continue;
}
[UsedImplicitly]
[GameEventHandler]
public HookResult OnTakeDamage(EventPlayerHurt ev, GameEventInfo info) {
var player = ev.Userid;
var attacker = ev.Attacker;
if (player == null || !player.IsReal()) return HookResult.Continue;
if (!ShouldBlockDamage(player, attacker)) return HookResult.Continue;
if (player.PlayerPawn.IsValid) {
var playerPawn = player.PlayerPawn.Value!;
playerPawn.Health = playerPawn.LastHealth;
}
info.DontBroadcast = false;
ev.DmgArmor = ev.DmgHealth = 0;
return HookResult.Handled;
}
[GameEventHandler]
public HookResult OnRoundEnd(EventRoundEnd @event, GameEventInfo info) {
foreach (var lr in ActiveLRs.ToList())
EndLastRequest(lr, LRResult.TIMED_OUT);
IsLREnabled = false;
return HookResult.Continue;
}
@@ -485,6 +491,10 @@ public class LastRequestManager(ILRLocale messages, IServiceProvider provider)
IsLREnabled = false;
foreach (var player in Utilities.GetPlayers())
MenuManager.CloseActiveMenu(player);
foreach (var lr in ActiveLRs.ToList())
EndLastRequest(lr, LRResult.TIMED_OUT);
ActiveLRs.Clear();
return HookResult.Continue;
}
@@ -558,4 +568,10 @@ public class LastRequestManager(ILRLocale messages, IServiceProvider provider)
if (!player.PawnIsAlive) return false;
return player.Team == CsTeam.Terrorist;
}
private void addRoundTimeCapped(int time, int max) {
var timeleft = RoundUtil.GetTimeRemaining();
if (timeleft + time > max) time = max - timeleft;
RoundUtil.AddTimeRemaining(time);
}
}

View File

@@ -1,6 +1,8 @@
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes.Registration;
using CounterStrikeSharp.API.Modules.Commands;
using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.Utils;
using Jailbreak.Formatting.Extensions;
using Jailbreak.Formatting.Views.LastRequest;
@@ -13,6 +15,30 @@ namespace Jailbreak.LastRequest;
public class LastRequestRebelCommand(ILastRequestManager lastRequestManager,
ILastRequestRebelManager lastRequestRebelManager, ILRLocale messages)
: IPluginBehavior {
private readonly Dictionary<int, int> rebellerHealths = [];
public void Start(BasePlugin basePlugin) {
basePlugin.RegisterListener<Listeners.OnEntityParentChanged>(OnDrop);
}
private void OnDrop(CEntityInstance entity, CEntityInstance newparent) {
if (!entity.IsValid || !Tag.WEAPONS.Contains(entity.DesignerName)) return;
var weapon = Utilities.GetEntityFromIndex<CCSWeaponBase>((int)entity.Index);
if (weapon == null
|| weapon.PrevOwner.Get()?.OriginalController.Get() == null)
return;
var owner = weapon.PrevOwner.Get()?.OriginalController.Get();
if (owner == null || newparent.IsValid) return;
if (!rebellerHealths.TryGetValue(owner.Slot, out var hp)) return;
if (owner.Pawn.Value != null)
owner.SetHealth(Math.Min(hp, owner.Pawn.Value.Health));
rebellerHealths.Remove(owner.Slot);
}
[ConsoleCommand("css_rebel", "Rebel during last request as a prisoner")]
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_ONLY)]
public void Command_Rebel(CCSPlayerController? rebeller, CommandInfo info) {
@@ -38,12 +64,24 @@ 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);
}
[GameEventHandler]
public HookResult OnRoundEnd(EventRoundEnd @event, GameEventInfo info) {
lastRequestRebelManager.ClearLRRebelling();
rebellerHealths.Clear();
return HookResult.Continue;
}
}

View File

@@ -3,13 +3,13 @@ using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Cvars;
using CounterStrikeSharp.API.Modules.Cvars.Validators;
using CounterStrikeSharp.API.Modules.Menu;
using Jailbreak.Public.Mod.LastRequest;
using Jailbreak.Public.Mod.Rebel;
using Jailbreak.Validator;
using CounterStrikeSharp.API.Modules.Utils;
using Jailbreak.Formatting.Extensions;
using Jailbreak.Formatting.Views.LastRequest;
using Jailbreak.Public.Extensions;
using Jailbreak.Public.Mod.LastRequest;
using Jailbreak.Public.Mod.Rebel;
using Jailbreak.Validator;
namespace Jailbreak.LastRequest;
@@ -27,12 +27,12 @@ public class LastRequestRebelManager(IRebelService rebelService,
ConVarFlags.FCVAR_NONE, new RangeValidator<int>(1, 1000));
public static readonly FakeConVar<double> CV_T_HEALTH_RATIO = new(
"css_jb_rebel_t_hp_ratio", "Ratio of T : CT Health", 0.5,
"css_jb_rebel_t_hp_ratio", "Ratio of T : CT Health", 0.7,
ConVarFlags.FCVAR_NONE, new RangeValidator<double>(0.00001, 10));
public static readonly FakeConVar<int> CV_MAX_T_HEALTH =
new("css_jb_rebel_t_max_hp", "Max HP that the rebeller can have otherwise",
125, ConVarFlags.FCVAR_NONE, new RangeValidator<int>(1, 1000));
800, ConVarFlags.FCVAR_NONE, new RangeValidator<int>(1, 1000));
public HashSet<int> PlayersLRRebelling { get; } = [];

View File

@@ -34,7 +34,7 @@ public class BulletForBullet(BasePlugin plugin, IServiceProvider provider,
=> magForMag ? LRType.MAG_FOR_MAG : LRType.SHOT_FOR_SHOT;
public override void Setup() {
Plugin.RegisterEventHandler<EventBulletImpact>(OnPlayerShoot);
Plugin.RegisterEventHandler<EventWeaponFire>(OnWeaponFire);
Prisoner.RemoveWeapons();
Guard.RemoveWeapons();
@@ -95,8 +95,7 @@ public class BulletForBullet(BasePlugin plugin, IServiceProvider provider,
}, TimerFlags.STOP_ON_MAPCHANGE);
}
private HookResult OnPlayerShoot(EventBulletImpact @event,
GameEventInfo info) {
private HookResult OnWeaponFire(EventWeaponFire @event, GameEventInfo info) {
if (State != LRState.ACTIVE) return HookResult.Continue;
var player = @event.Userid;
@@ -122,8 +121,17 @@ public class BulletForBullet(BasePlugin plugin, IServiceProvider provider,
}
public override void OnEnd(LRResult result) {
Plugin.DeregisterEventHandler<EventBulletImpact>(OnPlayerShoot);
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

@@ -20,27 +20,19 @@ public class GunToss(BasePlugin plugin, ILastRequestManager manager,
IServiceProvider provider, CCSPlayerController prisoner,
CCSPlayerController guard)
: TeleportingRequest(plugin, manager, prisoner, guard), IDropListener {
private readonly List<BeamLine> guardLines = [], prisonerLines = [];
private readonly ILRGunTossLocale locale =
provider.GetRequiredService<ILRGunTossLocale>();
/// <summary>
/// Null if no one has thrown a gun yet, negative if only one has thrown a gun,
/// Positive if both have thrown a gun.
/// </summary>
private int? bothThrewTick;
private Timer? guardGroundTimer, prisonerGroundTimer;
private Timer? guardGunTimer, prisonerGunTimer;
private bool guardTossed, prisonerTossed;
public override LRType Type => LRType.GUN_TOSS;
private CCSWeaponBase? prisonerWeapon, guardWeapon;
public override LRType Type => LRType.GUN_TOSS;
public void OnWeaponDrop(CCSPlayerController player, CCSWeaponBase weapon) {
if (bothThrewTick > 0) return;
if (State != LRState.ACTIVE) return;
if (State != LRState.ACTIVE || !player.IsValid) return;
bothThrewTick = bothThrewTick switch {
null => -Server.TickCount,
@@ -51,37 +43,35 @@ public class GunToss(BasePlugin plugin, ILastRequestManager manager,
if (player == Prisoner) {
prisonerTossed = true;
prisonerWeapon = weapon;
} else {
}
if (player == Guard) {
guardTossed = true;
guardWeapon = weapon;
}
if (Guard.Slot == Prisoner.Slot) bothThrewTick = Server.TickCount;
if (bothThrewTick > 0) {
if (prisonerTossed && guardTossed) bothThrewTick = Server.TickCount;
if (bothThrewTick > 0)
Plugin.AddTimer(5, () => {
if (State != LRState.ACTIVE) return;
if (Guard.PlayerPawn.Value != null)
Guard.PlayerPawn.Value.TakesDamage = true;
Prisoner.GetWeaponBase("weapon_deagle").SetAmmo(7, 35);
if (State != LRState.ACTIVE || !Guard.IsValid || !Guard.Pawn.IsValid) return;
Guard.SetHealth(Math.Min(Guard.Pawn.Value!.Health, 100));
Guard.SetArmor(Math.Min(Guard.PawnArmor, 100));
});
}
followWeapon(player, weapon);
if (player == Prisoner)
prisonerGroundTimer?.Kill();
else
guardGroundTimer?.Kill();
}
public override void Setup() {
base.Setup();
if (Guard.PlayerPawn.Value != null)
Guard.PlayerPawn.Value.TakesDamage = false;
Prisoner.RemoveWeapons();
Guard.RemoveWeapons();
Server.NextFrame(() => {
if (!Guard.IsValid) return;
Guard.SetHealth(500);
Guard.SetArmor(500);
});
Plugin.AddTimer(3, Execute);
}
@@ -91,156 +81,12 @@ public class GunToss(BasePlugin plugin, ILastRequestManager manager,
Guard.GiveNamedItem("weapon_knife");
Prisoner.GiveNamedItem("weapon_deagle");
Guard.GiveNamedItem("weapon_deagle");
Prisoner.GetWeaponBase("weapon_deagle").SetAmmo(2, 0);
Prisoner.GetWeaponBase("weapon_deagle").SetAmmo(0, 7);
Server.NextFrame(() => State = LRState.ACTIVE);
followPlayer(Prisoner);
if (Guard.Slot != Prisoner.Slot) followPlayer(Guard);
Plugin.RegisterListener<Listeners.OnTick>(OnTick);
Server.RunOnTick(Server.TickCount + 16, () => State = LRState.ACTIVE);
}
private void OnTick() {
if (bothThrewTick > 0) return;
if (Guard is { IsValid: true, PlayerPawn.IsValid: true }
&& Guard.PlayerPawn.Value != null)
if ((Guard.PlayerPawn.Value.Flags & (uint)PlayerFlags.FL_ONGROUND) != 0)
onGround(Guard);
if (Prisoner is { IsValid: true, PlayerPawn.IsValid: true }
&& Prisoner.PlayerPawn.Value != null)
if ((Prisoner.PlayerPawn.Value.Flags & (uint)PlayerFlags.FL_ONGROUND)
!= 0)
onGround(Prisoner);
}
private void onGround(CCSPlayerController player) {
if (bothThrewTick > 0 || State != LRState.ACTIVE) {
Plugin.RemoveListener<Listeners.OnTick>(OnTick);
return;
}
if (player.Slot == Prisoner.Slot && prisonerTossed) return;
if (player.Slot == Guard.Slot && guardTossed) return;
var lines = player.Slot == Prisoner.Slot ? prisonerLines : guardLines;
lines.ForEach(l => l.Remove());
lines.Clear();
}
public override void OnEnd(LRResult result) {
State = LRState.COMPLETED;
Plugin.RemoveListener<Listeners.OnTick>(OnTick);
guardGroundTimer?.Kill();
prisonerGroundTimer?.Kill();
guardGunTimer?.Kill();
prisonerGunTimer?.Kill();
guardGroundTimer = null;
prisonerGroundTimer = null;
guardGunTimer = null;
prisonerGunTimer = null;
guardLines.ForEach(l => l.Remove());
guardLines.Clear();
prisonerLines.ForEach(l => l.Remove());
prisonerLines.Clear();
}
private void followPlayer(CCSPlayerController player) {
var lines = player == Prisoner ? prisonerLines : guardLines;
Vector? previous = null;
var timer = Plugin.AddTimer(0.1f, () => {
if (State != LRState.ACTIVE) {
lines.ForEach(l => l.Remove());
lines.Clear();
prisonerGroundTimer?.Kill();
guardGroundTimer?.Kill();
prisonerGroundTimer = null;
guardGroundTimer = null;
return;
}
Debug.Assert(player.PlayerPawn.Value != null,
"player.PlayerPawn.Value != null");
if ((player.PlayerPawn.Value.Flags & (uint)PlayerFlags.FL_ONGROUND)
!= 0) {
// Player is on the ground
lines.ForEach(l => l.Remove());
lines.Clear();
return;
}
var position = player.PlayerPawn.Value?.AbsOrigin!.Clone()!
+ new Vector(0, 0, 64);
if (previous != null) {
var line = new BeamLine(Plugin, previous, position);
line.SetColor(player == Prisoner ? Color.Red : Color.Blue);
line.SetWidth(1f);
line.Draw(25);
lines.Add(line);
}
previous = position.Clone();
}, TimerFlags.REPEAT | TimerFlags.STOP_ON_MAPCHANGE);
if (player.Slot == Prisoner.Slot)
prisonerGroundTimer = timer;
else
guardGroundTimer = timer;
}
private void followWeapon(CCSPlayerController player, CCSWeaponBase weapon) {
var lines = player.Slot == Prisoner.Slot ? prisonerLines : guardLines;
var lastPos = lines.Count > 0 ? lines[^1].End : null;
Debug.Assert(player.PlayerPawn.Value != null,
"player.PlayerPawn.Value != null");
var timer = Plugin.AddTimer(0.1f, () => {
if (weapon.AbsOrigin == null || !weapon.IsValid) {
if (player.Slot == Prisoner.Slot) {
prisonerGunTimer?.Kill();
prisonerWeapon = null;
} else {
guardGunTimer?.Kill();
guardWeapon = null;
}
return;
}
if (lastPos != null && lastPos.DistanceSquared(weapon.AbsOrigin) == 0) {
if (player.Slot == Prisoner.Slot) {
prisonerGunTimer?.Kill();
prisonerGunTimer = null;
} else {
guardGunTimer?.Kill();
guardGunTimer = null;
}
var firstPos = lines[0].Position;
locale.PlayerThrewGunDistance(player, lastPos.Distance(firstPos))
.ToAllChat();
return;
}
if (lastPos != null) {
var line = new BeamLine(Plugin, lastPos, weapon.AbsOrigin);
line.SetColor(player == Prisoner ? Color.DarkRed : Color.DarkBlue);
line.SetWidth(0.5f);
line.Draw(25);
lines.Add(line);
}
lastPos = weapon.AbsOrigin.Clone();
}, TimerFlags.REPEAT | TimerFlags.STOP_ON_MAPCHANGE);
if (player.Slot == Prisoner.Slot)
prisonerGunTimer = timer;
else
guardGunTimer = timer;
}
public override void OnEnd(LRResult result) { State = LRState.COMPLETED; }
public override bool PreventEquip(CCSPlayerController player,
CCSWeaponBaseVData weapon) {
@@ -264,6 +110,6 @@ public class GunToss(BasePlugin plugin, ILastRequestManager manager,
if (bothThrewTick is null or < 0) return true;
var time = Server.TickCount - bothThrewTick.Value;
return time < 64 * 5;
return time < 64 * 4;
}
}

View File

@@ -37,7 +37,8 @@ public class Race(BasePlugin plugin, ILastRequestManager manager,
start = new BeamCircle(Plugin, startLocation, 20, 16);
start.SetColor(Color.Aqua);
start.Draw();
State = LRState.ACTIVE;
if (Guard.Pawn.Value != null) Guard.Pawn.Value.TakesDamage = false;
}
// Called when the prisoner types !endrace
@@ -52,7 +53,6 @@ public class Race(BasePlugin plugin, ILastRequestManager manager,
Prisoner.Pawn.Value?.Teleport(startLocation);
Guard.Pawn.Value?.Teleport(startLocation);
if (Guard.Pawn.Value != null) Guard.Pawn.Value.TakesDamage = false;
if (Prisoner.Pawn.Value != null) Prisoner.Pawn.Value.TakesDamage = false;
Guard.Freeze();
@@ -69,6 +69,11 @@ public class Race(BasePlugin plugin, ILastRequestManager manager,
}
private void tick() {
if (!Prisoner.IsValid || !Guard.IsValid) {
Manager.EndLastRequest(this, LRResult.INTERRUPTED);
return;
}
if (Prisoner.AbsOrigin == null || Guard.AbsOrigin == null) return;
var requiredDistance = getRequiredDistance();
var requiredDistanceSqured = MathF.Pow(requiredDistance, 2);

View File

@@ -2,14 +2,13 @@
using Jailbreak.Public.Extensions;
using Jailbreak.Public.Mod.LastRequest;
using Jailbreak.Public.Mod.LastRequest.Enums;
using Jailbreak.Public.Mod.Weapon;
namespace Jailbreak.LastRequest.LastRequests;
public abstract class TeleportingRequest(BasePlugin plugin,
ILastRequestManager manager, CCSPlayerController prisoner,
CCSPlayerController guard)
: AbstractLastRequest(plugin, manager, prisoner, guard), IEquipBlocker {
: AbstractLastRequest(plugin, manager, prisoner, guard) {
public override void Setup() {
State = LRState.PENDING;

View File

@@ -33,25 +33,30 @@ public class LogDamageListeners : IPluginBehavior {
var health = @event.DmgHealth;
if (isWorld) {
if (health > 0)
logs.Append("The world hurt", logs.Player(player),
$"for {health} damage");
else
logs.Append("The world killed", logs.Player(player));
logs.Append("The world hurt", logs.Player(player),
$"for {health} damage");
} else {
if (attacker == null) {
logs.Append(logs.Player(player), "was hurt by an unknown source",
$"for {health} damage");
return HookResult.Continue;
}
if (health > 0)
logs.Append(logs.Player(attacker), "hurt", logs.Player(player),
$"for {health} damage");
else
logs.Append(logs.Player(attacker), "killed", logs.Player(player));
logs.Append(logs.Player(attacker!), "hurt", logs.Player(player),
$"for {health} damage");
}
return HookResult.Continue;
}
[GameEventHandler]
public HookResult OnPlayerDeath(EventPlayerDeath @event, GameEventInfo info) {
var player = @event.Userid;
if (player == null || !player.IsReal()) return HookResult.Continue;
var attacker = @event.Attacker;
var isWorld = attacker == null || !attacker.IsReal();
if (isWorld) {
logs.Append("The world killed", logs.Player(player));
} else {
logs.Append(logs.Player(attacker!), "killed", logs.Player(player));
}
return HookResult.Continue;
}
}

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

@@ -1,4 +1,5 @@
using CounterStrikeSharp.API;
using System.Collections.Concurrent;
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes.Registration;
using CounterStrikeSharp.API.Modules.Admin;
@@ -17,20 +18,27 @@ namespace Jailbreak.RTD;
public class AutoRTD(IRTDRewarder rewarder, IAutoRTDLocale locale,
IRTDLocale rtdLocale, IGenericCmdLocale generic) : IPluginBehavior {
private static readonly Dictionary<ulong, bool> cachedCookies = new();
private static readonly ConcurrentDictionary<ulong, bool> cachedCookies = new();
public static readonly FakeConVar<string> CV_AUTORTD_FLAG =
new("css_autortd_flag", "Permission flag required to enable auto-RTD",
"@ego/dssilver");
private BasePlugin plugin = null!;
private ICookie? cookie;
public void Start(BasePlugin basePlugin) {
Task.Run(async () => {
if (API.Actain != null)
cookie = await API.Actain.getCookieService()
.RegClientCookie("jb_rtd_auto");
});
plugin = basePlugin;
TryLoadCookie();
basePlugin.RegisterListener<Listeners.OnMapStart>(OnMapStart);
}
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);
}
[GameEventHandler]
@@ -56,17 +64,6 @@ public class AutoRTD(IRTDRewarder rewarder, IAutoRTDLocale locale,
return HookResult.Continue;
}
private async Task populateCache(CCSPlayerController player, ulong steam) {
if (cookie == null) return;
var val = await cookie.Get(steam);
cachedCookies[steam] = val is null or "Y";
if (!cachedCookies[steam]) return;
await Server.NextFrameAsync(() => {
if (!player.IsValid) return;
player.ExecuteClientCommandFromServer("css_rtd");
});
}
[ConsoleCommand("css_autortd")]
public void Command_AutoRTD(CCSPlayerController? executor, CommandInfo info) {
if (executor == null) return;
@@ -98,4 +95,23 @@ public class AutoRTD(IRTDRewarder rewarder, IAutoRTDLocale locale,
});
});
}
private void TryLoadCookie() {
Task.Run(async () => {
if (API.Actain != null)
cookie = await API.Actain.getCookieService()
.RegClientCookie("jb_rtd_auto");
});
}
private async Task populateCache(CCSPlayerController player, ulong steam) {
if (cookie == null) return;
var val = await cookie.Get(steam);
cachedCookies[steam] = val is null or "Y";
if (!cachedCookies[steam]) return;
await Server.NextFrameAsync(() => {
if (!player.IsValid) return;
player.ExecuteClientCommandFromServer("css_rtd");
});
}
}

View File

@@ -57,36 +57,38 @@ public class RewardGenerator(IC4Service bomb, IWardenSelectionService warden,
(new HPReward(150), PROB_MEDIUM), (new HPReward(50), PROB_MEDIUM),
(new ArmorReward(150), PROB_MEDIUM),
(new GuaranteedWardenReward(warden), PROB_MEDIUM),
(new WeaponReward("weapon_g3sg1", CsTeam.CounterTerrorist), PROB_MEDIUM),
(new WeaponReward("weapon_g3sg1", CsTeam.CounterTerrorist),
PROB_MEDIUM / 2),
// Low
(new AmmoWeaponReward("weapon_glock", 0, 0), PROB_LOW * 2f),
(new AmmoWeaponReward("weapon_glock", 0, 0), PROB_LOW),
(new ChatSpyReward(basePlugin), PROB_LOW * 1.5f),
(new ColorReward(Color.FromArgb(0, 255, 0), true), PROB_LOW),
(new ColorReward(Color.FromArgb(255, 0, 0), true), PROB_LOW),
// (new ColorReward(Color.FromArgb(255, 0, 0), true), PROB_LOW),
(new CannotUseReward(basePlugin, WeaponType.GRENADE), PROB_LOW),
(new CannotScope(basePlugin), PROB_LOW),
(new CannotRightKnife(basePlugin), PROB_LOW),
(new AmmoWeaponReward("weapon_glock", 2, 0), PROB_LOW),
(new AmmoWeaponReward("weapon_negev", 0, 6), PROB_LOW),
(new CannotUseReward(basePlugin, WeaponType.SNIPERS), PROB_LOW),
(new CannotUseReward(basePlugin, WeaponType.HEAVY), PROB_LOW),
(new TransparentReward(), PROB_LOW / 2),
(new AmmoWeaponReward("weapon_glock", 2, 0), PROB_LOW / 2),
(new AmmoWeaponReward("weapon_negev", 0, 6), PROB_LOW / 2),
(new CreditReward(50, locale), PROB_LOW), (new HPReward(1), PROB_LOW / 2),
// Very low
(new FakeBombReward(), PROB_VERY_LOW * 2),
(new CannotLeftKnife(basePlugin), PROB_VERY_LOW),
(new NoWeaponReward(), PROB_VERY_LOW),
(new AmmoWeaponReward("weapon_deagle", 1, 0), PROB_VERY_LOW),
(new CannotUseReward(basePlugin, WeaponType.SMGS), PROB_VERY_LOW),
(new CannotUseReward(basePlugin, WeaponType.PISTOLS), PROB_VERY_LOW),
(new CannotUseReward(basePlugin, WeaponType.RIFLES), PROB_VERY_LOW),
(new RandomTeleportReward(provider.GetService<IZoneManager>()),
PROB_VERY_LOW),
(new BombReward(bomb), PROB_VERY_LOW),
(new CreditReward(-100, locale), PROB_VERY_LOW),
(new CreditReward(500, locale), PROB_VERY_LOW),
(new AmmoWeaponReward("weapon_awp", 1, 0), PROB_VERY_LOW / 2),
(new AmmoWeaponReward("weapon_deagle", 1, 0), PROB_VERY_LOW / 2),
(new AmmoWeaponReward("weapon_awp", 1, 0), PROB_VERY_LOW / 4),
// Extremely low
(new CannotUseReward(basePlugin, WeaponType.KNIVES), PROB_EXTREMELY_LOW),

View File

@@ -8,11 +8,6 @@ namespace Jailbreak.RTD.Rewards;
public class AmmoWeaponReward : WeaponReward {
private readonly int primary, secondary;
public override string Name
=> primary + secondary == 0 ?
$"Toy {weapon.GetFriendlyWeaponName()}" :
$"{weapon.GetFriendlyWeaponName()} ({primary}/{secondary})";
public AmmoWeaponReward(string weapon, int primary, int secondary,
CsTeam requiredTeam = CsTeam.Terrorist) : base(weapon, requiredTeam) {
Trace.Assert(Tag.GUNS.Contains(weapon));
@@ -20,6 +15,11 @@ public class AmmoWeaponReward : WeaponReward {
this.secondary = secondary;
}
public override string Name
=> primary + secondary == 0 ?
$"Toy {weapon.GetFriendlyWeaponName()}" :
$"{weapon.GetFriendlyWeaponName()} ({primary}/{secondary})";
public override bool GrantReward(CCSPlayerController player) {
player.GiveNamedItem(weapon);
player.GetWeaponBase(weapon).SetAmmo(primary, secondary);

View File

@@ -1,5 +1,4 @@
using System.Collections.Immutable;
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
@@ -10,8 +9,8 @@ using Jailbreak.Public.Mod.RTD;
namespace Jailbreak.RTD.Rewards;
public class CannotUseReward : IRTDReward {
private readonly HashSet<int> blockedIDs = [];
private readonly ImmutableHashSet<string> blockedWeapons;
private readonly BasePlugin plugin;
public CannotUseReward(BasePlugin plugin, WeaponType blocked) : this(plugin,
blocked.GetItems().ToArray()) {
@@ -19,7 +18,6 @@ public class CannotUseReward : IRTDReward {
}
public CannotUseReward(BasePlugin plugin, params string[] weapons) {
this.plugin = plugin;
blockedWeapons = weapons.ToImmutableHashSet();
NameShort = string.Join(", ",
blockedWeapons.Select(s => s.GetFriendlyWeaponName()));
@@ -28,6 +26,23 @@ public class CannotUseReward : IRTDReward {
plugin.RegisterEventHandler<EventRoundEnd>(OnRoundEnd);
}
public string NameShort { get; }
public string Name => $"Cannot Use {NameShort}";
public string Description
=> $"You will not be able to use {NameShort} next round.";
public bool GrantReward(CCSPlayerController player) {
if (player.UserId == null) return false;
blockedIDs.Add(player.UserId.Value);
if (blockedWeapons.Any(w => w.Contains("knife") || w.Contains("bayonet")))
player.RemoveWeapons();
return true;
}
private HookResult OnRoundEnd(EventRoundEnd @event, GameEventInfo info) {
blockedIDs.Clear();
return HookResult.Continue;
@@ -52,22 +67,4 @@ public class CannotUseReward : IRTDReward {
hook.SetReturn(AcquireResult.NotAllowedByMode);
return HookResult.Handled;
}
public string Name => $"Cannot Use {NameShort}";
public string NameShort { get; }
public string Description
=> $"You will not be able to use {NameShort} next round.";
private readonly HashSet<int> blockedIDs = [];
public bool GrantReward(CCSPlayerController player) {
if (player.UserId == null) return false;
blockedIDs.Add(player.UserId.Value);
if (blockedWeapons.Any(w => w.Contains("knife") || w.Contains("bayonet")))
player.RemoveWeapons();
return true;
}
}

View File

@@ -15,14 +15,17 @@ using Jailbreak.Formatting.Views;
using Jailbreak.Public;
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;
namespace Jailbreak.Rebel.C4Bomb;
public class C4Behavior(IC4Locale ic4Locale, IRebelService rebelService)
: IPluginBehavior, IC4Service {
public class C4Behavior(IC4Locale ic4Locale, IRebelService rebelService,
IServiceProvider provider) : IPluginBehavior, IC4Service {
public static readonly FakeConVar<bool> CV_GIVE_BOMB = new("css_jb_c4_give",
"Whether to give a random prisoner a bomb at the beginning of the round.",
true);
@@ -40,17 +43,11 @@ public class C4Behavior(IC4Locale ic4Locale, IRebelService rebelService)
new RangeValidator<float>(0, 10000));
private readonly Dictionary<CC4, C4Metadata> bombs = new();
private readonly Dictionary<ulong, string> cachedBombIcons = new();
// 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 int roundStart = 0;
private readonly Dictionary<int, int> deathToKiller = new();
private bool giveNextRound = true;
private BasePlugin? plugin;
@@ -76,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() {
@@ -109,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
@@ -135,6 +134,7 @@ public class C4Behavior(IC4Locale ic4Locale, IRebelService rebelService)
[GameEventHandler]
public HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info) {
roundStart = Server.TickCount;
ClearActiveC4s();
refreshBombIcons();
@@ -172,11 +172,11 @@ public class C4Behavior(IC4Locale ic4Locale, IRebelService rebelService)
continue;
}
var (success, data) =
var data =
await gangStats.GetForGang<BombPerkData>(gangPlayer.GangId.Value,
BombPerk.STAT_ID);
if (!success || data == null || data.Equipped == 0)
if (data == null || data.Equipped == 0)
cachedGangBombIcons[gangPlayer.GangId.Value] = "";
else
cachedGangBombIcons[gangPlayer.GangId.Value] =
@@ -229,27 +229,24 @@ public class C4Behavior(IC4Locale ic4Locale, IRebelService rebelService)
private void detonate(CCSPlayerController player, CC4 bomb) {
if (!player.IsValid || !player.IsReal() || !player.PawnIsAlive) {
bombs.TryGetValue(bomb, out _);
if (bomb.IsValid) bomb.Remove();
bombs.Remove(bomb);
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();
if (Server.TickCount - roundStart < CV_C4_DELAY.Value * 64) return;
var killed = 0;
/* Calculate damage here, only applies to alive CTs. */
var lrs = provider.GetRequiredService<ILastRequestManager>();
foreach (var ct in Utilities.GetPlayers()
.Where(p => p is { Team: CsTeam.CounterTerrorist, PawnIsAlive: true })) {
var lr = lrs.GetActiveLR(ct);
if (lr != null) {
var otherLr = lrs.GetActiveLR(player);
if (otherLr == null || otherLr != lr) continue;
}
var distanceFromBomb =
ct.PlayerPawn.Value!.AbsOrigin!.Distance(player.PlayerPawn.Value
.AbsOrigin!);
@@ -269,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) {
@@ -278,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

@@ -1,31 +1,22 @@
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.Menu;
using Jailbreak.Formatting.Extensions;
using Jailbreak.Formatting.Views.SpecialDay;
using Jailbreak.Formatting.Views.Warden;
using Jailbreak.Public.Behaviors;
using Jailbreak.Public.Mod.SpecialDay;
using Jailbreak.Public.Mod.SpecialDay.Enums;
using Jailbreak.Public.Mod.Warden;
using Jailbreak.Public.Utils;
using Jailbreak.SpecialDay.SpecialDays;
namespace Jailbreak.SpecialDay;
public class SpecialDayCommand(IWardenService warden,
ISpecialDayFactory factory, IWardenLocale wardenMsg, ISDLocale sdMsg,
public class SpecialDayCommand(ISpecialDayFactory factory, ISDLocale sdMsg,
ISpecialDayManager sd) : IPluginBehavior {
public static readonly FakeConVar<int> CV_ROUNDS_BETWEEN_SD = new(
"css_jb_sd_round_cooldown", "Rounds between special days", 4);
public static readonly FakeConVar<int> CV_MAX_ELAPSED_TIME = new(
"css_jb_sd_max_elapsed_time",
"Max time elapsed in a round to be able to call a special day", 30);
"css_jb_sd_round_cooldown", "Rounds between special days", 5);
private SpecialDayMenuSelector menuSelector = null!;
private BasePlugin plugin = null!;
@@ -52,36 +43,6 @@ public class SpecialDayCommand(IWardenService warden,
return;
}
if (executor != null
&& !AdminManager.PlayerHasPermissions(executor, "@css/rcon")) {
if (!warden.IsWarden(executor) || RoundUtil.IsWarmup()) {
wardenMsg.NotWarden.ToChat(executor);
return;
}
if (sd.IsSDRunning) {
// SD is already running
if (sd.CurrentSD is ISpecialDayMessageProvider messaged)
sdMsg.SpecialDayRunning(messaged.Locale.Name).ToChat(executor);
else
sdMsg.SpecialDayRunning(sd.CurrentSD?.Type.ToString() ?? "Unknown")
.ToChat(executor);
return;
}
var roundsToNext = sd.RoundsSinceLastSD - CV_ROUNDS_BETWEEN_SD.Value;
if (roundsToNext < 0) {
sdMsg.SpecialDayCooldown(Math.Abs(roundsToNext)).ToChat(executor);
return;
}
if (RoundUtil.GetTimeElapsed() > CV_MAX_ELAPSED_TIME.Value) {
sdMsg.TooLateForSpecialDay(CV_MAX_ELAPSED_TIME.Value);
return;
}
}
if (info.ArgCount == 1) {
if (executor == null) {
Server.PrintToConsole("css_sd [SD]");
@@ -100,6 +61,8 @@ public class SpecialDayCommand(IWardenService warden,
return;
}
var canStart = sd.CanStartSpecialDay(type.Value, executor);
if (!canStart) return;
sd.InitiateSpecialDay(type.Value);
}
}

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

@@ -1,6 +1,8 @@
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes.Registration;
using CounterStrikeSharp.API.Modules.Admin;
using CounterStrikeSharp.API.Modules.Cvars;
using CounterStrikeSharp.API.Modules.Utils;
using Gangs.BaseImpl.Extensions;
using Gangs.SpecialDayColorPerk;
@@ -8,11 +10,14 @@ using GangsAPI.Data;
using GangsAPI.Services.Gang;
using GangsAPI.Services.Player;
using Jailbreak.Formatting.Extensions;
using Jailbreak.Formatting.Views.SpecialDay;
using Jailbreak.Formatting.Views.Warden;
using Jailbreak.Public;
using Jailbreak.Public.Extensions;
using Jailbreak.Public.Mod.Rainbow;
using Jailbreak.Public.Mod.SpecialDay;
using Jailbreak.Public.Mod.SpecialDay.Enums;
using Jailbreak.Public.Mod.Warden;
using Jailbreak.Public.Utils;
using Jailbreak.SpecialDay.SpecialDays;
using Microsoft.Extensions.DependencyInjection;
@@ -21,7 +26,15 @@ using MStatsShared;
namespace Jailbreak.SpecialDay;
public class SpecialDayManager(ISpecialDayFactory factory,
IServiceProvider provider) : ISpecialDayManager {
IServiceProvider provider, IWardenLocale wardenMsg, ISDLocale locale)
: ISpecialDayManager {
public static readonly FakeConVar<int> CV_MAX_ELAPSED_TIME = new(
"css_jb_sd_max_elapsed_time",
"Max time elapsed in a round to be able to call a special day", 30);
public static readonly FakeConVar<int> CV_ROUNDS_BETWEEN_SD = new(
"css_jb_sd_round_cooldown", "Rounds between special days", 5);
private readonly IRainbowColorizer colorizer =
provider.GetRequiredService<IRainbowColorizer>();
@@ -29,6 +42,51 @@ public class SpecialDayManager(ISpecialDayFactory factory,
public AbstractSpecialDay? CurrentSD { get; private set; }
public int RoundsSinceLastSD { get; set; }
public bool CanStartSpecialDay(SDType type, CCSPlayerController? player,
bool print = true) {
var warden = provider.GetRequiredService<IWardenService>();
if (!AdminManager.PlayerHasPermissions(player, "@css/rcon")) {
if (!warden.IsWarden(player) || RoundUtil.IsWarmup()) {
if (print) wardenMsg.NotWarden.ToChat(player);
return false;
}
if (IsSDRunning) {
if (CurrentSD is ISpecialDayMessageProvider messaged) {
if (print)
locale.SpecialDayRunning(messaged.Locale.Name).ToChat(player);
return false;
}
if (print)
locale.SpecialDayRunning(CurrentSD?.Type.ToString() ?? "Unknown")
.ToChat(player);
return false;
}
var roundsToNext = RoundsSinceLastSD - CV_ROUNDS_BETWEEN_SD.Value;
if (roundsToNext < 0) {
if (print)
locale.SpecialDayCooldown(Math.Abs(roundsToNext)).ToChat(player);
return false;
}
if (RoundUtil.GetTimeElapsed() > CV_MAX_ELAPSED_TIME.Value) {
if (print)
locale.TooLateForSpecialDay(CV_MAX_ELAPSED_TIME.Value).ToChat(player);
return false;
}
}
var denyReason = type.CanCall(player);
if (denyReason == null
|| AdminManager.PlayerHasPermissions(player, "@css/root"))
return true;
if (print) locale.CannotCallDay(denyReason).ToChat(player);
return false;
}
public bool InitiateSpecialDay(SDType type) {
API.Stats?.PushStat(new ServerStat("JB_SPECIALDAY", type.ToString()));
RoundsSinceLastSD = 0;
@@ -56,9 +114,9 @@ public class SpecialDayManager(ISpecialDayFactory factory,
var gangPlayer = await players.GetPlayer(wrapper.Steam);
if (gangPlayer?.GangId == null) return;
var gangId = gangPlayer.GangId.Value;
var (success, data) =
var data =
await gangStats.GetForGang<SDColorData>(gangId, SDColorPerk.STAT_ID);
if (!success || data == null) continue;
if (data == null) continue;
var col = data.Equipped.GetColor() ?? data.Unlocked.PickRandom();

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

@@ -118,12 +118,13 @@ public class GunGameDay(BasePlugin plugin, IServiceProvider provider)
var attacker = @event.Attacker;
info.DontBroadcast = true;
if (player == null || !player.IsValid) return HookResult.Continue;
int playerIndex;
if (!progressions.TryGetValue(player.Slot, out playerIndex))
if (!progressions.TryGetValue(player.Slot, out var playerIndex))
playerIndex = 0;
if (attacker == null || !attacker.IsValid) return HookResult.Continue;
if (attacker.Slot == player.Slot) return HookResult.Continue;
@event.FireEventToClient(attacker);
var attackerProgress =
progressions.TryGetValue(attacker.Slot, out var attackerIndex) ?
attackerIndex :
@@ -137,6 +138,9 @@ public class GunGameDay(BasePlugin plugin, IServiceProvider provider)
p.RemoveWeapons();
}
VirtualFunctions.CCSPlayer_ItemServices_CanAcquireFunc.Unhook(
OnCanAcquire, HookMode.Pre);
attacker.SetSpeed(2f);
attacker.RemoveWeapons();
attacker.GiveNamedItem("weapon_negev");

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

@@ -8,12 +8,10 @@ using Jailbreak.Formatting.Base;
using Jailbreak.Formatting.Extensions;
using Jailbreak.Formatting.Views.SpecialDay;
using Jailbreak.Public.Extensions;
using Jailbreak.Public.Mod.LastRequest;
using Jailbreak.Public.Mod.SpecialDay;
using Jailbreak.Public.Mod.SpecialDay.Enums;
using Jailbreak.Public.Utils;
using Jailbreak.Validator;
using Microsoft.Extensions.DependencyInjection;
namespace Jailbreak.SpecialDay.SpecialDays;
@@ -149,8 +147,8 @@ public class HideAndSeekDay(BasePlugin plugin, IServiceProvider provider)
private readonly ISet<string>? cachedGuardWeapons, cachedPrisonerWeapons;
public HnsSettings() {
TTeleport = TeleportType.ARMORY;
CtTeleport = TeleportType.ARMORY;
TTeleport = TeleportType.ARMORY;
CtTeleport = TeleportType.ARMORY;
cachedGuardWeapons = CV_GUARD_WEAPONS.Value.Split(",").ToHashSet();
cachedPrisonerWeapons = CV_PRISONER_WEAPONS.Value.Split(",").ToHashSet();

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,28 +120,25 @@ 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;
}
public class InfectionSettings : SpecialDaySettings {
public InfectionSettings() {
CtTeleport = TeleportType.ARMORY;
TTeleport = TeleportType.RANDOM;
CtTeleport = TeleportType.ARMORY;
TTeleport = TeleportType.RANDOM;
WithRespawns(CsTeam.CounterTerrorist);
}
public override ISet<string>? AllowedWeapons(CCSPlayerController player) {
return player.Team == CsTeam.CounterTerrorist ?
Tag.UTILITY.Union(Tag.PISTOLS).ToHashSet() :
Tag.UTILITY.Union(Tag.KNIVES).Union(Tag.PISTOLS).ToHashSet() :
null;
}

View File

@@ -1,9 +1,6 @@
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Cvars;
using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
using CounterStrikeSharp.API.Modules.Utils;
using Jailbreak.English.SpecialDay;
using Jailbreak.Formatting.Extensions;
using Jailbreak.Formatting.Views.SpecialDay;

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

@@ -123,9 +123,8 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
generics = Provider.GetRequiredService<IGenericCmdLocale>();
foreach (var player in Utilities.GetPlayers()
.Where(p => p is { Team: CsTeam.Terrorist or CsTeam.CounterTerrorist })) {
.Where(p => p is { Team: CsTeam.Terrorist or CsTeam.CounterTerrorist }))
player.Respawn();
}
speedrunner = getRunner();
@@ -782,14 +781,13 @@ public class SpeedrunDay(BasePlugin plugin, IServiceProvider provider)
private int getEliminations(int players) {
return players switch {
<= 3 => 1,
<= 4 => 2,
<= 8 => 4,
<= 12 => 5,
<= 20 => 10,
<= 35 => 12,
<= 40 => 15,
<= 64 => 30,
<= 4 => 1,
<= 8 => 3,
<= 12 => 4,
<= 20 => 5,
<= 35 => 8,
<= 40 => 10,
<= 64 => 15,
_ => players / 5
};
}

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

@@ -16,19 +16,11 @@ public class SoccerCommandBehavior(IWardenService warden,
new("css_jb_max_soccers",
"The maximum number of soccer balls that the warden can spawn", 3);
private int soccers;
public void Start(BasePlugin basePlugin) {
basePlugin.RegisterListener<Listeners.OnServerPrecacheResources>(manifest
=> {
manifest.AddResource(
"models/props/de_dust/hr_dust/dust_soccerball/dust_soccer_ball001.vmdl");
});
}
private int soccerBalls;
[GameEventHandler]
public HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info) {
soccers = 0;
soccerBalls = 0;
return HookResult.Continue;
}
@@ -42,7 +34,7 @@ public class SoccerCommandBehavior(IWardenService warden,
return;
}
if (soccers >= CV_MAX_SOCCERS.Value) {
if (soccerBalls >= CV_MAX_SOCCERS.Value) {
locale.TooManySoccers.ToChat(player);
return;
}
@@ -66,6 +58,6 @@ public class SoccerCommandBehavior(IWardenService warden,
ball.Teleport(loc);
locale.SoccerSpawned.ToAllChat();
ball.DispatchSpawn();
soccers++;
soccerBalls++;
}
}

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

@@ -28,17 +28,15 @@ public class SpecialIconBehavior(ITextSpawner spawner)
if (playerStats == null) return SpecialIcon.DEFAULT.GetIcon();
var (success, icon) =
var icon =
await playerStats.GetForPlayer<SpecialIcon>(player,
SpecialIconPerk.STAT_ID);
if (!success) icon = SpecialIcon.DEFAULT;
if (gangStats == null || players == null) return icon.GetIcon();
var gangPlayer = await players.GetPlayer(player.Steam);
if (gangPlayer?.GangId == null) return SpecialIcon.DEFAULT.GetIcon();
var (_, available) =
var available =
await gangStats.GetForGang<SpecialIcon>(gangPlayer.GangId.Value,
SpecialIconPerk.STAT_ID);

View File

@@ -70,7 +70,7 @@ public class WardenBehavior(ILogger<WardenBehavior> logger,
public static readonly FakeConVar<bool> CV_WARDEN_AUTO_SNITCH =
new("css_jb_warden_auto_snitch",
"True to broadcast how many prisoners were in cells when they auto-open",
true);
false);
public static readonly FakeConVar<int> CV_WARDEN_HEALTH =
new("css_jb_warden_hp", "HP for the warden", 125, ConVarFlags.FCVAR_NONE,
@@ -106,7 +106,7 @@ public class WardenBehavior(ILogger<WardenBehavior> logger,
private BasePlugin parent = null!;
private PreWardenStats? preWardenStats;
private Timer? unblueTimer, openCellsTimer;
private Timer? unblueTimer, openCellsTimer, passFreedayTimer;
public void Start(BasePlugin basePlugin) {
parent = basePlugin;
@@ -170,10 +170,9 @@ public class WardenBehavior(ILogger<WardenBehavior> logger,
var stats = API.Gangs.Services.GetService<IPlayerStatManager>();
if (stats != null)
Task.Run(async () => {
var (success, stat) =
await stats.GetForPlayer<WardenData>(wrapper, WardenStat.STAT_ID);
if (!success || stat == null) stat = new WardenData();
var stat =
await stats.GetForPlayer<WardenData>(wrapper, WardenStat.STAT_ID)
?? new WardenData();
stat.TimesWardened++;
await stats.SetForPlayer(wrapper, WardenStat.STAT_ID, stat);
@@ -188,6 +187,8 @@ public class WardenBehavior(ILogger<WardenBehavior> logger,
logs.Append(logs.Player(Warden), "is now the warden.");
unblueTimer = parent.AddTimer(3, unmarkPrisonersBlue);
passFreedayTimer
?.Kill(); // If a warden is assigned, cancel the 10s freeday timer on pass
mute.PeaceMute(firstWarden ?
MuteReason.INITIAL_WARDEN :
MuteReason.WARDEN_TAKEN);
@@ -240,6 +241,11 @@ public class WardenBehavior(ILogger<WardenBehavior> logger,
HasWarden = false;
if (isPass) { // If passing, start the timer to announce freeday
passFreedayTimer =
parent.AddTimer(10, () => { locale.NowFreeday.ToAllChat(); });
}
if (Warden != null && Warden.Pawn.Value != null) {
Warden.Clan = "";
Warden.SetColor(Color.White);
@@ -298,9 +304,8 @@ public class WardenBehavior(ILogger<WardenBehavior> logger,
var stats = API.Gangs?.Services.GetService<IPlayerStatManager>();
if (stats == null) return;
var (success, stat) =
await stats.GetForPlayer<WardenData>(player, WardenStat.STAT_ID);
if (!success || stat == null) stat = new WardenData();
var stat = await stats.GetForPlayer<WardenData>(player, WardenStat.STAT_ID)
?? new WardenData();
stat.WardenDeaths++;
await stats.SetForPlayer(player, WardenStat.STAT_ID, stat);
@@ -356,10 +361,9 @@ public class WardenBehavior(ILogger<WardenBehavior> logger,
var stats = API.Gangs?.Services.GetService<IPlayerStatManager>();
if (stats == null) return;
await Task.Run(async () => {
var (success, stat) =
await stats.GetForPlayer<WardenData>(attacker, WardenStat.STAT_ID);
if (!success || stat == null) stat = new WardenData();
var stat =
await stats.GetForPlayer<WardenData>(attacker, WardenStat.STAT_ID)
?? new WardenData();
stat.WardensKilled++;
await stats.SetForPlayer(attacker, WardenStat.STAT_ID, stat);
@@ -371,10 +375,8 @@ public class WardenBehavior(ILogger<WardenBehavior> logger,
var stats = API.Gangs?.Services.GetService<IPlayerStatManager>();
if (stats == null) return;
var (success, stat) =
await stats.GetForPlayer<WardenData>(player, WardenStat.STAT_ID);
if (!success || stat == null) stat = new WardenData();
var stat = await stats.GetForPlayer<WardenData>(player, WardenStat.STAT_ID)
?? new WardenData();
if (isWarden)
// The warden let a guard die
@@ -393,7 +395,7 @@ public class WardenBehavior(ILogger<WardenBehavior> logger,
if (API.Actain != null) {
var steam = player.SteamID;
Server.NextFrameAsync(async () => {
Task.Run(async () => {
if ("[WARDEN]" != await API.Actain.getTagService().GetTag(steam))
return;
Server.NextFrame(() => {
@@ -425,8 +427,6 @@ public class WardenBehavior(ILogger<WardenBehavior> logger,
$"play sounds/{CV_WARDEN_SOUND_KILLED.Value}");
}
locale.BecomeNextWarden.ToAllChat();
unblueTimer
?.Kill(); // If the warden dies withing 3 seconds of becoming warden, we need to cancel the unblue timer
markPrisonersBlue();
@@ -597,9 +597,8 @@ public class WardenBehavior(ILogger<WardenBehavior> logger,
var gangId = gangPlayer?.GangId;
if (gangId == null) continue;
var (success, cells) =
var cells =
await gangStats.GetForGang<int>(gangId.Value, CellsPerk.STAT_ID);
if (!success) cells = 0;
gangMembers.TryGetValue(gangId.Value, out var count);
gangMembers[gangId.Value] = ++count;

View File

@@ -28,17 +28,15 @@ public class WardenIconBehavior(ITextSpawner spawner)
if (playerStats == null) return WardenIcon.DEFAULT.GetIcon();
var (success, icon) =
var icon =
await playerStats.GetForPlayer<WardenIcon>(player,
WardenIconPerk.STAT_ID);
if (!success) icon = WardenIcon.DEFAULT;
if (gangStats == null || players == null) return icon.GetIcon();
var gangPlayer = await players.GetPlayer(player.Steam);
if (gangPlayer?.GangId == null) return WardenIcon.DEFAULT.GetIcon();
var (_, available) =
var available =
await gangStats.GetForGang<WardenIcon>(gangPlayer.GangId.Value,
WardenIconPerk.STAT_ID);

View File

@@ -4,16 +4,17 @@
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\public\Jailbreak.Formatting\Jailbreak.Formatting.csproj"/>
<ProjectReference Include="..\..\public\Jailbreak.Public\Jailbreak.Public.csproj"/>
<ProjectReference Include="..\Gangs.CellsPerk\Gangs.CellsPerk.csproj"/>
<ProjectReference Include="..\Gangs.SpecialIconPerk\Gangs.SpecialIconPerk.csproj"/>
<ProjectReference Include="..\Gangs.WardenIconPerk\Gangs.WardenIconPerk.csproj"/>
<ProjectReference Include="..\Jailbreak.Zones\Jailbreak.Zones.csproj"/>
<ProjectReference Include="..\Gangs.WardenPaintColorPerk\Gangs.WardenPaintColorPerk.csproj"/>
<ProjectReference Include="..\..\public\Jailbreak.Formatting\Jailbreak.Formatting.csproj" />
<ProjectReference Include="..\..\public\Jailbreak.Public\Jailbreak.Public.csproj" />
<ProjectReference Include="..\Gangs.CellsPerk\Gangs.CellsPerk.csproj" />
<ProjectReference Include="..\Gangs.SpecialIconPerk\Gangs.SpecialIconPerk.csproj" />
<ProjectReference Include="..\Gangs.WardenIconPerk\Gangs.WardenIconPerk.csproj" />
<ProjectReference Include="..\Jailbreak.Zones\Jailbreak.Zones.csproj" />
<ProjectReference Include="..\Gangs.WardenPaintColorPerk\Gangs.WardenPaintColorPerk.csproj" />
</ItemGroup>
<ItemGroup>
@@ -28,4 +29,9 @@
</Reference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="CS2ScreenMenuAPI" Version="2.6.0" />
<PackageReference Include="CS2TraceRay" Version="1.0.8" />
</ItemGroup>
</Project>

View File

@@ -1,18 +1,31 @@
using System.Drawing;
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes.Registration;
using CounterStrikeSharp.API.Modules.Commands;
using CounterStrikeSharp.API.Modules.Cvars;
using CounterStrikeSharp.API.Modules.Menu;
using CounterStrikeSharp.API.Modules.Timers;
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;
using Jailbreak.Public.Behaviors;
using Jailbreak.Public.Extensions;
using Jailbreak.Public.Mod.Draw;
using Jailbreak.Public.Mod.Warden;
using Jailbreak.Warden.Paint;
using MStatsShared;
using PostSelectAction = CS2ScreenMenuAPI.Enums.PostSelectAction;
namespace Jailbreak.Warden.Markers;
public class WardenMarkerBehavior(IWardenService warden)
public class WardenMarkerBehavior(IWardenService warden, IWardenLocale locale)
: IPluginBehavior, IMarkerService {
public static readonly FakeConVar<float> CV_MAX_RADIUS = new(
"css_jb_warden_marker_max_radius", "Maximum radius for warden marker", 360);
@@ -24,20 +37,138 @@ public class WardenMarkerBehavior(IWardenService warden)
"css_jb_warden_resize_time", "Milliseconds to wait for resizing marker",
800);
// private Vector? MarkerPosition;
private BeamCircle[] markers = [];
private BeamCircle tmpMarker = null!;
private readonly string[] markerNames = [
ChatColors.Red + "Red", ChatColors.Green + "Green",
ChatColors.Blue + "Blue", ChatColors.Purple + "Purple"
];
private BeamCircle? marker;
private long placementTime;
public Vector? MarkerPosition { get; private set; }
public float radius { get; private set; }
// private float radius;
private bool activelyPlacing, removedMarker;
private ScreenMenu menu = null!;
private BasePlugin plugin = null!;
public void Start(BasePlugin basePlugin) {
marker = new BeamCircle(basePlugin, new Vector(), CV_MIN_RADIUS.Value,
(int)Math.PI * 15);
plugin = basePlugin;
tmpMarker = new BeamCircle(basePlugin, new Vector(), CV_MIN_RADIUS.Value,
10);
markers = [
new BeamCircle(basePlugin, new Vector(), CV_MIN_RADIUS.Value, 10),
new BeamCircle(basePlugin, new Vector(), CV_MIN_RADIUS.Value, 10),
new BeamCircle(basePlugin, new Vector(), CV_MIN_RADIUS.Value, 10),
new BeamCircle(basePlugin, new Vector(), CV_MIN_RADIUS.Value, 10)
];
markers[0].SetColor(Color.Red);
markers[1].SetColor(Color.Green);
markers[2].SetColor(Color.Blue);
markers[3].SetColor(Color.Purple);
menu = new ScreenMenu("Markers", basePlugin) {
PostSelectAction = PostSelectAction.Close,
MenuType = MenuType.KeyPress,
PositionX = -8.5f
};
menu.AddOption("Red", (_, _) => placeMarker(0));
menu.AddOption("Green", (_, _) => placeMarker(1));
menu.AddOption("Blue", (_, _) => placeMarker(2));
menu.AddOption("Purple", (_, _) => placeMarker(3));
basePlugin.AddCommandListener("player_ping", CommandListener_PlayerPing);
// basePlugin.AddTimer(0.1f, OnTick, TimerFlags.REPEAT);
}
private void placeMarker(int index) {
if (MarkerPosition == null) return;
var marker = markers[index];
marker.Move(MarkerPosition);
marker.SetRadius(radius);
marker.Update();
tmpMarker.SetRadius(1);
tmpMarker.Move(MarkerPosition);
tmpMarker.Update();
MarkerPosition = null;
locale.MarkerPlaced(markerNames[index]).ToAllChat();
}
[GameEventHandler]
public HookResult OnSpawn(EventPlayerSpawn spawn, GameEventInfo info) {
if (spawn.Userid == null || !spawn.Userid.IsValid)
return HookResult.Continue;
SetBinds(spawn.Userid);
return HookResult.Continue;
}
private void OnTick() {
if (!warden.HasWarden) return;
if (warden.Warden == null || !warden.Warden.IsReal()) return;
if ((warden.Warden.Buttons & PlayerButtons.Attack2) == 0) {
if (activelyPlacing && !removedMarker) {
MenuAPI.CloseActiveMenu(warden.Warden);
Server.NextFrame(() => MenuAPI.OpenMenu(plugin, warden.Warden, menu));
}
activelyPlacing = false;
removedMarker = false;
return;
}
var weapon = warden.Warden.Pawn.Value?.WeaponServices?.ActiveWeapon.Value
?.DesignerName;
if (weapon != null && Tag.SNIPERS.Contains(weapon)
|| weapon == "weapon_sg556")
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++) {
var marker = markers[i];
var dist = marker.Position.DistanceSquared(position);
if (!(dist < MathF.Pow(marker.Radius, 2))) continue;
marker.SetRadius(0);
marker.Update();
locale.MarkerRemoved(markerNames[i]).ToAllChat();
removedMarker = true;
return;
}
}
if (removedMarker) return;
if (MarkerPosition == null || !activelyPlacing) {
tmpMarker.SetRadius(CV_MIN_RADIUS.Value);
tmpMarker.Move(position);
tmpMarker.Update();
MarkerPosition = position;
activelyPlacing = true;
placementTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
API.Stats?.PushStat(new ServerStat("JB_MARKER",
$"{position.X:F2} {position.Y:F2} {position.Z:F2}"));
radius = CV_MIN_RADIUS.Value;
return;
}
var distance = MarkerPosition.Distance(position);
distance = Math.Clamp(distance, CV_MIN_RADIUS.Value, CV_MAX_RADIUS.Value);
radius = distance;
tmpMarker.SetRadius(distance);
tmpMarker.Update();
}
[GameEventHandler]
@@ -55,13 +186,13 @@ public class WardenMarkerBehavior(IWardenService warden)
if (distance <= CV_MAX_RADIUS.Value * 1.3) {
distance = Math.Clamp(distance, CV_MIN_RADIUS.Value,
CV_MAX_RADIUS.Value);
marker?.SetRadius(distance);
marker?.Update();
tmpMarker?.SetRadius(distance);
tmpMarker?.Update();
radius = distance;
return HookResult.Handled;
}
} else if (distance <= radius) {
marker?.Remove();
tmpMarker?.Remove();
return HookResult.Handled;
}
}
@@ -72,9 +203,9 @@ public class WardenMarkerBehavior(IWardenService warden)
API.Stats?.PushStat(new ServerStat("JB_MARKER",
$"{vec.X:F2} {vec.Y:F2} {vec.Z:F2}"));
marker?.Move(vec);
marker?.SetRadius(radius);
marker?.Update();
tmpMarker?.Move(vec);
tmpMarker?.SetRadius(radius);
tmpMarker?.Update();
return HookResult.Handled;
}
@@ -82,4 +213,9 @@ public class WardenMarkerBehavior(IWardenService warden)
CommandInfo info) {
return warden.IsWarden(player) ? HookResult.Continue : HookResult.Handled;
}
public static void SetBinds(CCSPlayerController player) {
for (var i = 0; i < 10; i++)
player.ExecuteClientCommand($"echo test | bind {i} \"slot{i};css_{i}\"");
}
}

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 = findFloorIntersection(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();
@@ -66,6 +74,7 @@ public class WardenPaintBehavior(IWardenService wardenService,
var color = getColor(warden);
var line = new BeamLine(parent, start.Clone(), position.Clone());
line.SetColor(color);
line.SetWidth(1.5f);
line.Draw(30);
}
@@ -105,11 +114,8 @@ public class WardenPaintBehavior(IWardenService wardenService,
|| gangStats == null)
return WardenPaintColor.DEFAULT;
var (success, playerColors) =
await playerStats.GetForPlayer<WardenPaintColor>(player.Steam,
WardenPaintColorPerk.WardenPaintColorPerk.STAT_ID);
if (!success) return WardenPaintColor.DEFAULT;
var playerColors = await playerStats.GetForPlayer<WardenPaintColor>(
player.Steam, WardenPaintColorPerk.WardenPaintColorPerk.STAT_ID);
var gangPlayer = await players.GetPlayer(player.Steam);
@@ -118,7 +124,7 @@ public class WardenPaintBehavior(IWardenService wardenService,
var gang = await gangs.GetGang(gangPlayer.GangId.Value);
if (gang == null) return WardenPaintColor.DEFAULT;
var (_, available) = await gangStats.GetForGang<WardenPaintColor>(gang,
var available = await gangStats.GetForGang<WardenPaintColor>(gang,
WardenPaintColorPerk.WardenPaintColorPerk.STAT_ID);
if (playerColors == WardenPaintColor.RANDOM)
@@ -126,60 +132,4 @@ public class WardenPaintBehavior(IWardenService wardenService,
return playerColors & available;
}
private Vector? findFloorIntersection(CCSPlayerController player) {
if (player.Pawn.Value == null || player.PlayerPawn.Value == null)
return null;
var pawn = player.Pawn.Value;
var playerPawn = player.PlayerPawn.Value;
if (pawn == null || !pawn.IsValid || !playerPawn.IsValid
|| pawn.CameraServices == null)
return null;
if (pawn.AbsOrigin == null) return null;
var camera = pawn.CameraServices;
var cameraOrigin = new Vector(pawn.AbsOrigin!.X, pawn.AbsOrigin!.Y,
pawn.AbsOrigin!.Z + camera.OldPlayerViewOffsetZ);
var eyeAngle = player.PlayerPawn.Value.EyeAngles;
var pitch = Math.PI / 180 * eyeAngle.X;
var yaw = Math.PI / 180 * eyeAngle.Y;
// get direction vector from angles
var eyeVector = new Vector((float)(Math.Cos(yaw) * Math.Cos(pitch)),
(float)(Math.Sin(yaw) * Math.Cos(pitch)), (float)-Math.Sin(pitch));
return findFloorIntersection(cameraOrigin, eyeVector,
new Vector(eyeAngle.X, eyeAngle.Y, eyeAngle.Z), pawn.AbsOrigin.Z);
}
private Vector? findFloorIntersection(Vector start, Vector worldAngles,
Vector rotationAngles, float z) {
var pitch =
rotationAngles
.X; // 90 = straight down, -90 = straight up, 0 = straight ahead
// normalize so 0 = straight down, 180 = straight up, 90 = straight ahead
pitch = 90 - pitch;
if (pitch >= 90) return null;
// var angleA = ToRadians(90);
var sideB = start.Z - z;
var angleC = toRadians(pitch);
var angleB = 180 - 90 - pitch;
var sideA = sideB * MathF.Sin(toRadians(90)) / MathF.Sin(toRadians(angleB));
var sideC = MathF.Sqrt(sideB * sideB + sideA * sideA
- 2 * sideB * sideA * MathF.Cos(angleC));
var destination = start.Clone();
destination.X += worldAngles.X * sideC;
destination.Y += worldAngles.Y * sideC;
destination.Z = z;
return destination;
}
private static float toRadians(float angle) {
return (float)(Math.PI / 180) * angle;
}
}

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

@@ -131,8 +131,13 @@ public static class ViewExtensions {
}
public static IView ToChat(this IView view,
params CCSPlayerController[] players) {
foreach (var player in players) view.ToPlayerChat(player);
params CCSPlayerController?[] players) {
foreach (var player in players) {
if (player == null)
view.ToServerConsole();
else
view.ToPlayerChat(player);
}
return view;
}

View File

@@ -7,4 +7,5 @@ public interface ISDLocale {
public IView InvalidSpecialDay(string name);
public IView SpecialDayCooldown(int rounds);
public IView TooLateForSpecialDay(int maxTime);
public IView CannotCallDay(string reason);
}

View File

@@ -8,6 +8,7 @@ namespace Jailbreak.Formatting.Views.Warden;
public interface IWardenLocale {
public IView PickingShortly { get; }
public IView NoWardens { get; }
public IView NowFreeday { get; }
public IView WardenLeft { get; }
public IView WardenDied { get; }
public IView BecomeNextWarden { get; }
@@ -17,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
@@ -46,4 +49,10 @@ public interface IWardenLocale {
public IView FireWarden(CCSPlayerController player,
CCSPlayerController admin);
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

@@ -1,5 +1,4 @@
using CounterStrikeSharp.API.Core;
using Jailbreak.Public.Extensions;
namespace Jailbreak.Public.Mod.Damage;
@@ -8,12 +7,6 @@ namespace Jailbreak.Public.Mod.Damage;
/// taking damage.
/// </summary>
public interface IDamageBlocker {
[Obsolete("Do not use the EventPlayerHurt overload.")]
bool ShouldBlockDamage(CCSPlayerController victim,
CCSPlayerController? attacker, EventPlayerHurt @event) {
return ShouldBlockDamage(victim, attacker);
}
bool ShouldBlockDamage(CCSPlayerController victim,
CCSPlayerController? attacker);
}

View File

@@ -9,24 +9,26 @@ namespace Jailbreak.Public.Mod.Draw;
public class BeamCircle : BeamedShape {
private readonly BeamLine?[] lines;
private Vector[] offsets;
private float radius;
public float Radius { get; private set; }
public BeamCircle(BasePlugin plugin, Vector position, float radius,
int resolution) : base(plugin, position, resolution) {
this.radius = radius;
lines = new BeamLine[resolution];
Radius = radius;
lines = new BeamLine[resolution];
offsets = generateOffsets();
}
public float Width => 1.5f;
private float degToRadian(float d) { return (float)(d * (Math.PI / 180)); }
private Vector[] generateOffsets() {
var newOffsets = new Vector[lines.Length];
var angle = 360f / lines.Length;
for (var i = 0; i < lines.Length; i++) {
var x = radius * MathF.Cos(degToRadian(angle * i));
var y = radius * MathF.Sin(degToRadian(angle * i));
var x = Radius * MathF.Cos(degToRadian(angle * i));
var y = Radius * MathF.Sin(degToRadian(angle * i));
newOffsets[i] = new Vector(x, y, 0);
}
@@ -40,6 +42,7 @@ public class BeamCircle : BeamedShape {
var end = Position + offsets[(i + 1) % offsets.Length];
if (line == null) {
line = new BeamLine(Plugin, start, end);
line.SetWidth(Width);
line.SetColor(Color);
line.Draw();
lines[i] = line;
@@ -51,7 +54,7 @@ public class BeamCircle : BeamedShape {
}
public void SetRadius(float _radius) {
radius = _radius;
Radius = _radius;
offsets = generateOffsets();
}
}

View File

@@ -48,7 +48,8 @@ public class BeamLine(BasePlugin plugin, Vector position, Vector end)
public override void Remove() {
KillTimer?.Kill();
if (beam != null && beam.IsValid) beam?.Remove();
if (beam != null && beam.IsValid) beam.Remove();
beam = null;
}

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

@@ -1,16 +1,23 @@
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Admin;
using CounterStrikeSharp.API.Modules.Utils;
namespace Jailbreak.Public.Mod.SpecialDay.Enums;
public enum SDType {
CUSTOM,
BHOP,
FFA,
FOG,
GUNGAME,
GHOST,
HE,
HNS,
INFECTION,
NOSCOPE,
OITC,
PACMAN,
ROCKETJUMP,
SNAKE,
SPEEDRUN,
TAG,
@@ -30,9 +37,28 @@ 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
};
}
public static string?
CanCall(this SDType type, CCSPlayerController? executor) {
return type switch {
SDType.HNS or SDType.SPEEDRUN =>
AdminManager.PlayerHasPermissions(executor, "@ego/dssilver") ?
null :
$"You must be a {ChatColors.Green}Silver Supporter {ChatColors.Default}({ChatColors.LightBlue}https://edgm.rs/donate{ChatColors.Default}){ChatColors.Grey} to start this day.",
// SDType.SPEEDRUN =>
// AdminManager.PlayerHasPermissions(executor, "@ego/dsgold") ?
// null :
// $"You must be a {ChatColors.Gold}Gold Supporter {ChatColors.Default}({ChatColors.LightBlue}https://edgm.rs/donate{ChatColors.Default}){ChatColors.Grey} to start this day.",
_ => null
};
}
}

View File

@@ -1,3 +1,4 @@
using CounterStrikeSharp.API.Core;
using Jailbreak.Public.Behaviors;
using Jailbreak.Public.Mod.SpecialDay.Enums;
@@ -8,5 +9,15 @@ public interface ISpecialDayManager : IPluginBehavior {
public AbstractSpecialDay? CurrentSD { get; }
public int RoundsSinceLastSD { get; }
/// <summary>
/// Function to check if the player can start the specified special day,
/// returns a string explanation if the player cannot start the special day.
/// Returns null if the player can start the special day.
/// </summary>
/// <param name="type"></param>
/// <param name="player"></param>
/// <returns></returns>
bool CanStartSpecialDay(SDType type, CCSPlayerController? player, bool print = true);
bool InitiateSpecialDay(SDType type);
}

View File

@@ -2,7 +2,7 @@
public static class Tag {
/// <summary>
/// Items that can backstab
/// Items that can backstab
/// </summary>
public static readonly IReadOnlySet<string> KNIVES = new HashSet<string>([
"weapon_knife", "weapon_knife_bayonet", "weapon_knife_butterfly",
@@ -31,7 +31,7 @@ public static class Tag {
"weapon_healthshot", "item_assaultsuit", "item_kevlar",
"weapon_diversion",
"weapon_breachcharge", "weapon_bumpmine", "weapon_c4", "weapon_tablet",
"weapon_taser", "weapon_shield", "weapon_snowball",
"weapon_taser", "weapon_shield", "weapon_snowball"
]).Union(GRENADES)
.ToHashSet();

View File

@@ -39,6 +39,8 @@ public class Jailbreak : BasePlugin {
manifest.AddResource("soundevents/soundevents_jb.vsndevts");
manifest.AddResource("sounds/explosion.vsnd");
manifest.AddResource("sounds/jihad.vsnd");
manifest.AddResource(
"models/props/de_dust/hr_dust/dust_soccerball/dust_soccer_ball001.vmdl");
});
// Load Managers