Compare commits

...

140 commits

Author SHA1 Message Date
Eidolon
c8f986db4d Remove unintended g++ warnings 2025-09-30 20:18:47 -05:00
Eidolon
0734ffc201 Start MODVERSION 9 RC6 2025-09-30 20:12:04 -05:00
Eidolon
fa787c4e8f Merge remote-tracking branch 'public/master' 2025-09-30 17:37:01 -05:00
Gunla
511d9b083b 2.4 RC5 Hashes 2025-09-30 22:36:34 +00:00
Eidolon
6f61cfaefa Merge branch 'shitty-sign-markedfordeath' into 'master'
Add ability for shitty signs to display when 1st place finishes while markedfordeath

See merge request kart-krew-dev/ring-racers!28
2025-09-30 17:16:26 -05:00
Freaky Mutant Man
932deb0b3f Add ability for shitty signs to display when 1st place finishes while markedfordeath 2025-09-30 17:16:26 -05:00
Eidolon
2309d11274 Merge branch 'fixUnfrozenTickers' into 'master'
Freeze finish line when level is frozen (fixes #237)

Closes #237

See merge request kart-krew-dev/ring-racers!37
2025-09-30 17:13:43 -05:00
JugadorXEI
a9fe850eda Freeze finish line when level is frozen (fixes #237) 2025-09-30 17:13:43 -05:00
Eidolon
210ec5ee63 Merge branch 'classrnochal' into 'master'
Remove ability for Class R to clear Time Attack challenges

See merge request kart-krew-dev/ring-racers!41
2025-09-30 17:11:29 -05:00
Eidolon
cbb99ca79b Merge branch 'reducevfx-checkwarning' into 'master'
Disable flashing of CHECK warnings when reducevfx is on.

See merge request kart-krew-dev/ring-racers!43
2025-09-30 17:09:47 -05:00
Freaky Mutant Man
4ff1f38877 Disable flashing of CHECK warnings when reducevfx is on. 2025-09-30 17:09:46 -05:00
Eidolon
78b11e8306 Merge branch 'bailhornset' into 'master'
Bail horn respects follower horn settings (fixes #251)

Closes #251

See merge request kart-krew-dev/ring-racers!45
2025-09-30 17:06:27 -05:00
zander3312
eb90b28c12 Bail horn respects follower horn settings (fixes #251) 2025-09-30 17:06:27 -05:00
Eidolon
8bf15ef6f1 Merge branch 'insaneAsylumGremlin' into 'master'
Add workaround for gremlin'd wall collisions

See merge request kart-krew-dev/ring-racers!35
2025-09-30 17:05:35 -05:00
JugadorXEI
ef4ef91fed Add workaround for gremlin'd wall collisions 2025-09-30 17:05:35 -05:00
Eidolon
abbc6b03d4 Merge branch 'creditsmusic' into 'master'
Menu music plays after credits (fixes #189)

Closes #189

See merge request kart-krew-dev/ring-racers!48
2025-09-30 17:03:22 -05:00
zander3312
50f007b653 Menu music plays after credits (fixes #189) 2025-09-30 17:03:22 -05:00
Eidolon
11acb78875 Merge branch 'nogravbulbturbine' into 'master'
Removed nogravity flag on touching bulbs/turbines (fixes #245)

Closes #245

See merge request kart-krew-dev/ring-racers!46
2025-09-30 17:02:54 -05:00
zander3312
8363ead346 Removed nogravity flag on touching bulbs/turbines (fixes #245) 2025-09-30 17:02:54 -05:00
Antonio Martinez
32d03ccb1e go go gadget gameplay constants 2025-09-29 23:00:59 -04:00
Antonio Martinez
d5c4534d31 Marginal item power nudge 2025-09-29 18:26:46 -04:00
AJ Martinez
2d3e7ce049 Merge branch 'fix-rocket-bail' into 'master'
Really remove rocketsneaker while bailing

See merge request kart-krew-dev/ring-racers-internal!2914
2025-09-29 22:23:49 +00:00
AJ Martinez
14201c65aa Merge branch 'fix-damage-floor-invinc' into 'master'
Fix stumble behavior being used on non-stumble terrain

See merge request kart-krew-dev/ring-racers-internal!2913
2025-09-29 22:23:43 +00:00
AJ Martinez
093cdb1043 Merge branch 'slower-stone' into 'master'
Stone Shoe 80% -> 70% boostpower

See merge request kart-krew-dev/ring-racers-internal!2915
2025-09-29 22:23:36 +00:00
AJ Martinez
350cd423e4 Merge branch 'fix-debugitemodds' into 'master'
Fix debugitemodds displaying wrong odds

Closes ring-racers#227

See merge request kart-krew-dev/ring-racers-internal!2887
2025-09-29 22:23:29 +00:00
AJ Martinez
1d1e7d1741 Merge branch 'tripwire-deux' into 'master'
Race Tripwire 200% -> 250%, check larger scam window in larger games

See merge request kart-krew-dev/ring-racers-internal!2916
2025-09-29 22:23:22 +00:00
Antonio Martinez
59c4cfb2be Class R tripwire oopsie 2025-09-29 01:54:30 -04:00
Antonio Martinez
d3ac1a6c5e 200% tripwire in Tutorial / Easy 2025-09-29 01:19:56 -04:00
Antonio Martinez
60e65133e6 Race Tripwire 200% -> 250%, check larger scam window in larger games 2025-09-28 20:53:57 -04:00
Antonio Martinez
0badaed11d Stone Shoe 80% -> 70% boostpower 2025-09-28 20:33:55 -04:00
Antonio Martinez
53d71225b8 Really remove rocketsneaker while bailing 2025-09-28 20:24:41 -04:00
Antonio Martinez
80c0db9546 Fix stumble behavior being used on non-stumble terrain 2025-09-28 20:16:56 -04:00
AJ Martinez
06626c9b0d Merge branch 'staffsync-triage' into 'master'
Extended staffsync RNG/resync reporting

See merge request kart-krew-dev/ring-racers-internal!2912
2025-09-28 05:40:46 +00:00
Eidolon
0e46138a14 Fix ABI issue with skiprender 2025-09-28 00:00:21 -05:00
Antonio Martinez
ae0100ba05 Extended staffsync RNG/resync reporting 2025-09-27 23:09:52 -04:00
AJ Martinez
ab39175430 Merge branch 'fix-item-ties' into 'master'
Use cusval instead of cvmem to track item box break type (cvmem is used for VFX!)

Closes ring-racers#252

See merge request kart-krew-dev/ring-racers-internal!2910
2025-09-28 01:02:07 +00:00
AJ Martinez
9bdd01f5a6 Merge branch 'damaging-sector-stumble' into 'master'
Use damage stumble for stumble sectors / terrain again (allows DI)

See merge request kart-krew-dev/ring-racers-internal!2911
2025-09-28 01:02:00 +00:00
AJ Martinez
69e23ad50a Merge branch 'fix-lagless-sting' into 'master'
Fix hitlag from Ring Sting being zero

Closes #1666

See merge request kart-krew-dev/ring-racers-internal!2908
2025-09-28 01:01:48 +00:00
AJ Martinez
e3067e8a09 Merge branch 'staffsync-bodge' into 'master'
Disable PR_ITEM_DEBRIS detections in staffsync

See merge request kart-krew-dev/ring-racers-internal!2909
2025-09-28 00:38:57 +00:00
Antonio Martinez
3d16ecd7e9 Staffsync excludes many RNG classes 2025-09-27 20:38:42 -04:00
Antonio Martinez
bf901fddb3 Actually check the correct momentum, idiot 2025-09-27 06:11:34 -04:00
Antonio Martinez
140f9846aa Use damage stumble for stumble sectors / terrain again (allows DI) 2025-09-27 05:58:50 -04:00
Antonio Martinez
9297e3cdcb Use cusval instead of cvmem to track item box break type (cvmem is used for VFX!) 2025-09-27 02:58:22 -04:00
Antonio Martinez
7b10e4e2bd Largegamescaler slightly up 2025-09-26 23:40:16 -04:00
Antonio Martinez
667ad18339 Disable PR_ITEM_DEBRIS detections in staffsync 2025-09-26 23:35:31 -04:00
Antonio Martinez
5e44040ae8 Fix hitlag from Ring Sting being zero 2025-09-26 23:28:39 -04:00
AJ Martinez
55192eaf39 Merge branch 'fast-tripwire-entry' into 'master'
Fast Tripwire entry

See merge request kart-krew-dev/ring-racers-internal!2907
2025-09-26 22:42:52 +00:00
Antonio Martinez
d157076326 Fast Tripwire entry 2025-09-26 18:09:22 -04:00
AJ Martinez
8b86622218 Merge branch 'pain-with-gain' into 'master'
Remove ringboost only when taking non-stumble damage

Closes #1664

See merge request kart-krew-dev/ring-racers-internal!2903
2025-09-26 21:59:13 +00:00
AJ Martinez
5ead1e5964 Merge branch 'better-lightning-shield' into 'master'
Lightningcharge can punt, blocks

See merge request kart-krew-dev/ring-racers-internal!2906
2025-09-26 21:59:07 +00:00
AJ Martinez
d5fc805cbd Merge branch 'further-sting-leniency' into 'master'
Don't sting just after using rings

See merge request kart-krew-dev/ring-racers-internal!2905
2025-09-26 21:29:21 +00:00
Antonio Martinez
fbe2f9fd56 Lightningcharge can punt, blocks 2025-09-26 17:26:09 -04:00
AJ Martinez
7b4cf9a07c Merge branch 'no-fucking-mutibail-for-real' into 'master'
Cannot bail while bailing

Closes ring-racers#236

See merge request kart-krew-dev/ring-racers-internal!2896
2025-09-26 17:58:25 +00:00
AJ Martinez
d060df9bfe Merge branch 'clash-hitlag-deux' into 'master'
Reduce hitlag when punting hazards etc

Closes ring-racers#256

See merge request kart-krew-dev/ring-racers-internal!2898
2025-09-26 17:58:17 +00:00
AJ Martinez
a05a82de7b Merge branch 'skiprender' into 'master'
"skiprender" cvar (performance increase when fuzzing)

See merge request kart-krew-dev/ring-racers-internal!2901
2025-09-26 17:58:09 +00:00
AJ Martinez
cd038addd5 Merge branch 'proration-polish' into 'master'
Subsonic Adjustments

See merge request kart-krew-dev/ring-racers-internal!2886
2025-09-26 17:58:02 +00:00
Oni VelocitOni
8f4a698a35 Subsonic Adjustments 2025-09-26 17:58:02 +00:00
AJ Martinez
e7ee7ec9ce Merge branch 'allow-hitlag-explode' into 'master'
Fix Eggmark not activating on players in precisely timed hitlag

Closes ring-racers#259

See merge request kart-krew-dev/ring-racers-internal!2899
2025-09-26 17:57:53 +00:00
AJ Martinez
8b71cd9e82 Merge branch 'tutorial-password' into 'master'
Added password for unlocking all tutorials early

See merge request kart-krew-dev/ring-racers-internal!2880
2025-09-26 17:57:46 +00:00
AJ Martinez
3f8ee05db8 Merge branch 'acs-netlumps' into 'master'
ACS netlumps

See merge request kart-krew-dev/ring-racers-internal!2876
2025-09-26 17:57:39 +00:00
AJ Martinez
d55eab9748 Merge branch '99-laps' into 'master'
Duels end at 99 laps, high lapcount HUD safety

Closes ring-racers#107

See merge request kart-krew-dev/ring-racers-internal!2892
2025-09-26 17:57:29 +00:00
AJ Martinez
997f995ce9 Merge branch 'fix-mindelay-send' into 'master'
Fix mindelay wpnpref not being applied on join

Closes ring-racers#149

See merge request kart-krew-dev/ring-racers-internal!2890
2025-09-26 17:57:22 +00:00
AJ Martinez
bd4b3336bf Merge branch 'hell-respawn' into 'master'
Kill players who respawn at lethal threshold

Closes #1663

See merge request kart-krew-dev/ring-racers-internal!2900
2025-09-26 17:57:10 +00:00
AJ Martinez
a428811884 Merge branch 'acid-cancel-kickstart' into 'master'
Fix Acid Drop irregularities (multi-input, Kickstart, etc)

Closes ring-racers#181

See merge request kart-krew-dev/ring-racers-internal!2894
2025-09-26 17:57:04 +00:00
AJ Martinez
74ae7abd4c Merge branch 'eid-dumbass-titlescreen' into 'master'
Prevent 1-frame title screen when playing demos

Closes #1665

See merge request kart-krew-dev/ring-racers-internal!2904
2025-09-26 17:56:53 +00:00
Antonio Martinez
8649a66e08 Don't sting just after using rings 2025-09-26 13:52:42 -04:00
Eidolon
0c3906690d Merge branch 'eid-teamplay-demos-workaround' into 'master'
Workaround g_teamplay assigning teams erroneously in demos

Closes ring-racers#210

See merge request kart-krew-dev/ring-racers-internal!2902
2025-09-26 08:42:59 -05:00
Eidolon
8e4b690387 Prevent 1-frame title screen when playing demos
Nothing makes sense anymore

Fixes https://gitlab.com/kart-krew-dev/ring-racers-internal/-/issues/1665
2025-09-25 23:02:39 -05:00
Antonio Martinez
cfd8260762 Remove ringboost only when taking non-stumble damage 2025-09-25 23:17:19 -04:00
Eidolon
f2ab528062 Workaround g_teamplay assigning teams erroneously in demos
Fixes https://gitlab.com/kart-krew-dev/ring-racers/-/issues/210
2025-09-25 22:09:48 -05:00
Antonio Martinez
bf330bafcc Fix ringshooter death check to only activate if you are USING your ringshooter 2025-09-25 22:56:16 -04:00
Antonio Martinez
48e1ed5779 Don't die from Ring Shooter at -20 2025-09-25 21:58:45 -04:00
Antonio Martinez
5950298ac6 "skiprender" cvar (performance incerase when fuzzing) 2025-09-25 19:57:21 -04:00
Antonio Martinez
77768b5a68 Acid cancel last call 2025-09-25 19:47:36 -04:00
Antonio Martinez
c1a80e4b1b Kill players who die at lethal threshold 2025-09-25 19:15:46 -04:00
Antonio Martinez
65cf6edb93 Fix acid cancel oldbutton check 2025-09-25 18:45:11 -04:00
Eidolon
b746b1a0e4 Merge branch 'eid-battle-team-elim' into 'master'
Allow team victory by opposing team elimination in battle overtime

Closes ring-racers#204

See merge request kart-krew-dev/ring-racers-internal!2895
2025-09-25 17:04:06 -05:00
Antonio Martinez
b587b4b574 Fix Eggmark not activating on players in precisely timed hitlag 2025-09-25 18:02:19 -04:00
Eidolon
ddd48b72d7 Merge branch 'more-msvc' into 'master'
Add cpptrace in I_Error on windows, minidumps, stricter warnings, optimized drawers in debug

See merge request kart-krew-dev/ring-racers-internal!2881
2025-09-25 16:25:00 -05:00
Eidolon
94ef03dcd2 Add cpptrace in I_Error on windows, minidumps, stricter warnings, optimized drawers in debug 2025-09-25 16:08:09 -05:00
Antonio Martinez
a6f27a39e3 Overshield can punt 2025-09-25 16:49:11 -04:00
Antonio Martinez
22e7b5e8ad Reduce hitlag when punting hazards etc 2025-09-25 04:33:35 -04:00
Antonio Martinez
a31b4a2682 Cannot bail while bailing 2025-09-24 22:53:16 -04:00
Eidolon
c96e3b3cca Allow team victory by opposing team elimination in battle overtime 2025-09-24 21:49:19 -05:00
Antonio Martinez
766f34d4f5 Fix Kickstart being unable to Acid Drop with Strict Fastfall off 2025-09-24 22:41:42 -04:00
Eidolon
b7f8afd5ef Merge branch 'teamplay-monitor-items' into 'master'
Teamplay: Allow allies to pick up monitor paperitems

Closes ring-racers#217

See merge request kart-krew-dev/ring-racers-internal!2893
2025-09-24 21:36:15 -05:00
Antonio Martinez
6507984c22 Teamplay: Allow allies to pick up monitor paperitems 2025-09-24 22:25:20 -04:00
Antonio Martinez
3cc58d93f2 Whoops that's a testing define 2025-09-24 22:10:52 -04:00
Antonio Martinez
39c2779fb2 Duels end at 99 laps, high lapcount HUD safety 2025-09-24 22:09:43 -04:00
Eidolon
c1b22c0c73 Revert "Update vcpkg baseline"
This reverts commit b3a2cc2ea9.
2025-09-24 20:06:44 -05:00
Eidolon
dc1c3bec0f Merge branch 'eid-dequeue-once' into 'master'
Dequeue audio only once per frame, improve quality

Closes ring-racers#234

See merge request kart-krew-dev/ring-racers-internal!2889
2025-09-24 20:05:43 -05:00
Eidolon
e9906915b0 Calculate podium position for PODIUMCUP condition instead of using current player position
Fixes https://gitlab.com/kart-krew-dev/ring-racers/-/issues/253
2025-09-24 19:58:53 -05:00
AJ Martinez
8f213c7e1b Merge branch 'minor-stun-reduce' into 'master'
Min weight stun 10 sec -> 8 sec

See merge request kart-krew-dev/ring-racers-internal!2891
2025-09-24 21:34:35 +00:00
Antonio Martinez
cef307816d Min weight stun 10 sec -> 8 sec 2025-09-24 16:58:14 -04:00
Antonio Martinez
d69e667835 Fix mindelay wpnpref not being applied on join 2025-09-24 16:54:22 -04:00
Eidolon
b52e1cb61f Dequeue audio only once per frame, improve quality 2025-09-24 13:19:49 -05:00
AJ Martinez
fc4a2e9f80 Merge branch 'final-check-exp' into 'master'
"Final checkpoint" EXP bonus

See merge request kart-krew-dev/ring-racers-internal!2878
2025-09-24 04:25:59 +00:00
Antonio Martinez
01bcf6116e Copy-paste final lap PWR error 2025-09-24 00:10:36 -04:00
Antonio Martinez
15bac24b8d Fix deflated final checkpoint scoring 2025-09-23 22:47:40 -04:00
Antonio Martinez
c7fcf34a8a Vibetweaking 2025-09-23 22:12:38 -04:00
Antonio Martinez
d5f131dcc2 Convert displayexp math to fixed, use autocalibrated final checkpoint power 2025-09-23 21:26:51 -04:00
Ashnal
2be14316cf Finish position is now factored into exp 2025-09-23 21:26:51 -04:00
Antonio Martinez
60fca5210d "Final checkpoint" EXP bonus 2025-09-23 21:26:51 -04:00
AJ Martinez
7109cd8c13 Merge branch 'close-to-leader-items' into 'master'
Fewer speed items when crowding 1st

See merge request kart-krew-dev/ring-racers-internal!2888
2025-09-24 01:26:06 +00:00
Antonio Martinez
e0fd5b43c5 grug tweak exp influence 2025-09-23 21:25:08 -04:00
Antonio Martinez
51cc299e40 I guess we're just doing everything on vibes now huh 2025-09-23 21:17:19 -04:00
Antonio Martinez
84917e36f1 Not unused anymore, idiot 2025-09-23 21:10:07 -04:00
Antonio Martinez
5d30a46728 EXP softening, large game softening 2025-09-23 21:04:36 -04:00
Antonio Martinez
d0ef4cdfaf Grow stacking gives a maximum of 5 seconds grow time 2025-09-23 20:44:02 -04:00
Antonio Martinez
220f8b035d Fewer speed items when crowding 1st 2025-09-23 19:52:46 -04:00
Zander
51e22892ac removed ability for class r to clear ta challenges 2025-09-23 13:58:43 -04:00
Antonio Martinez
eb90dcd047 Fix debugitemodds displaying wrong odds 2025-09-23 06:53:02 -04:00
AJ Martinez
4337618342 Merge branch 'backline-exp-roundup' into 'master'
EXP adjustments

See merge request kart-krew-dev/ring-racers-internal!2879
2025-09-23 09:46:05 +00:00
AJ Martinez
2a72e0a855 Merge branch 'fix-ringboost-crack' into 'master'
Fix Ring payout rate getting stuck fast until interacting with aother Ring Box

Closes ring-racers#225

See merge request kart-krew-dev/ring-racers-internal!2884
2025-09-23 09:45:41 +00:00
AJ Martinez
6bd6c91077 Merge branch 'tutorial-flame' into 'master'
Max Flame Shield in Catcher/Tutorial

Closes ring-racers#219

See merge request kart-krew-dev/ring-racers-internal!2882
2025-09-23 09:44:59 +00:00
AJ Martinez
aee332fa9b Merge branch 'botslow-takeover-fix' into 'master'
Fix staff ghost desyncs when bots takeover

See merge request kart-krew-dev/ring-racers-internal!2885
2025-09-23 09:44:48 +00:00
AJ Martinez
f7bc6e303e Merge branch 'damage-fixes' into 'master'
Various damage fixes

Closes ring-racers#191, ring-racers#116, and ring-racers#197

See merge request kart-krew-dev/ring-racers-internal!2883
2025-09-23 09:44:36 +00:00
Antonio Martinez
4aa3309885 Oops deleted staffsync 2025-09-23 03:22:15 -04:00
Antonio Martinez
595874f4e5 CompatLevel fixes 2025-09-23 03:18:14 -04:00
Antonio Martinez
3f5f9825a8 Compatlevel for bumpslow fixes 2025-09-23 01:25:50 -04:00
Antonio Martinez
4478fce917 P_DamageMobjCompat 2025-09-23 01:25:32 -04:00
Antonio Martinez
f78ae582d4 Compatlevel for bumpslow fixes 2025-09-22 19:55:40 -04:00
Antonio Martinez
55b333e01f Remove sector damage stumble AND terrain damage stumble from damage system 2025-09-22 17:11:14 -04:00
Antonio Martinez
867dfb49f2 Stumble floors are not damage, we use this to control area access! 2025-09-22 15:56:32 -04:00
Antonio Martinez
3d5f0e9d73 Fix Ring payout rate getting stuck fast until interacting with aother Ring Box 2025-09-22 08:23:22 -04:00
Antonio Martinez
587eddeb70 Orbital/backspam downgrade cleanup 2025-09-22 04:41:42 -04:00
Antonio Martinez
4b94f06186 Various damage fixes 2025-09-22 04:19:30 -04:00
Antonio Martinez
38be6953e7 Max Flame Shield in Catcher/Tutorial 2025-09-22 01:15:03 -04:00
toaster
a13735822e Added password for unlocking all tutorials early 2025-09-21 12:11:01 +01:00
Eidolon
b3a2cc2ea9 Update vcpkg baseline 2025-09-20 20:39:09 -05:00
Antonio Martinez
0373a23d72 2 second FAULT 2025-09-20 19:18:24 -04:00
Antonio Martinez
27b9a46f0c Disable large game scaling in Frantic 2025-09-20 17:02:38 -04:00
Antonio Martinez
944c13993b EXP adjustments 2025-09-20 16:11:23 -04:00
Antonio Martinez
0dad1aa918 me sweepy 2025-09-19 23:51:39 -04:00
Antonio Martinez
f674778097 Super Ring is a speed item 2025-09-19 23:50:31 -04:00
toaster
9411ce5387 Environment::read/writeModuleName: Convert lumpnums to a netsafe form for transit
- Introduces new functions
    - W_LumpIntoNetSave
    - W_LumpFromNetSave
- Essentially shims the upper 16 bits to account for unimportant files
2025-09-19 16:22:06 +01:00
toaster
a01300cd54 w_wad for lumpnums: Use bitwise and instead of addition
Makes the mathematical logic of what's being done clearer
2025-09-19 16:12:59 +01:00
toaster
1517ba4d89 Fix mainwads-dependent calculations
- sounds.pk3 was being considered a mainwad in some spots due to use of <= instead of <
- fileneeded material was manually skipping over base musicwads when it didn't have to
2025-09-19 16:12:59 +01:00
AJ Martinez
4b81be037a Revert "Why no one noticed this I will never fully understand"
This reverts commit 75fe652dd5.
2025-09-18 23:12:17 -04:00
Antonio Martinez
75fe652dd5 Why no one noticed this I will never fully understand 2025-09-18 23:10:54 -04:00
67 changed files with 2279 additions and 436 deletions

View file

@ -125,6 +125,9 @@ find_package(PNG REQUIRED)
find_package(SDL2 CONFIG REQUIRED)
find_package(CURL REQUIRED)
find_package(Opus REQUIRED)
if(WIN32 AND NOT MINGW)
find_package(cpptrace CONFIG REQUIRED)
endif()
# Use the one in thirdparty/fmt to guarantee a minimum version
#find_package(FMT CONFIG REQUIRED)

View file

@ -54,8 +54,8 @@
"name": "__develop-cl",
"hidden": true,
"cacheVariables": {
"CMAKE_C_FLAGS_RELWITHDEBINFO": "/MD /O2 /Ob2 /DNDEBUG",
"CMAKE_CXX_FLAGS_RELWITHDEBINFO": "/MD /O2 /Ob2 /DNDEBUG",
"CMAKE_C_FLAGS_RELWITHDEBINFO": "/MD /Zi /O2 /Ob2 /DNDEBUG",
"CMAKE_CXX_FLAGS_RELWITHDEBINFO": "/MD /Zi /O2 /Ob2 /DNDEBUG",
"SRB2_CONFIG_DEV_BUILD": "ON",
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
}
@ -64,8 +64,8 @@
"name": "__release-cl",
"hidden": true,
"cacheVariables": {
"CMAKE_C_FLAGS_RELWITHDEBINFO": "/MD /O2 /Ob2 /DNDEBUG",
"CMAKE_CXX_FLAGS_RELWITHDEBINFO": "/MD /O2 /Ob2 /DNDEBUG",
"CMAKE_C_FLAGS_RELWITHDEBINFO": "/MD /Zi /O2 /Ob2 /DNDEBUG",
"CMAKE_CXX_FLAGS_RELWITHDEBINFO": "/MD /Zi /O2 /Ob2 /DNDEBUG",
"SRB2_CONFIG_DEV_BUILD": "OFF",
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
}

View file

@ -176,6 +176,41 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
p_deepcopy.cpp
)
# Always compile some files with optimizations in Debug config.
# This is to make the debugging experience less slow for the majority of cases.
set(always_optimize_options "")
if(MSVC)
set(always_optimize_options "$<$<CONFIG:Debug>:/O2;/Ob2>")
else()
set(always_optimize_options "$<$<CONFIG:Debug>:-O3>")
endif()
set_source_files_properties(
r_bsp.cpp
r_data.c
r_debug.cpp
r_debug_parser.cpp
r_debug_printer.cpp
r_draw.cpp
r_fps.cpp
r_main.cpp
r_plane.cpp
r_segs.cpp
r_skins.c
r_sky.c
r_splats.c
r_spritefx.cpp
r_things.cpp
r_bbox.c
r_textures.cpp
r_textures_dups.cpp
r_patch.cpp
r_patchrotation.c
r_picformats.c
r_portal.c
PROPERTIES
COMPILE_OPTIONS "${always_optimize_options}"
)
set_target_properties(SRB2SDL2 PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/sdl/macosx/MacOSXBundleInfo.plist.in")
if(SRB2_CONFIG_ENABLE_WEBM_MOVIES)
@ -210,7 +245,7 @@ add_custom_target(_SRB2_reconf ALL
)
add_dependencies(SRB2SDL2 _SRB2_reconf)
if(("${CMAKE_COMPILER_IS_GNUCC}" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") AND "${CMAKE_SYSTEM_NAME}" MATCHES "Windows")
if(("${CMAKE_COMPILER_IS_GNUCC}") AND "${CMAKE_SYSTEM_NAME}" MATCHES "Windows")
target_link_options(SRB2SDL2 PRIVATE "-Wl,--disable-dynamicbase")
if("${SRB2_CONFIG_STATIC_STDLIB}")
# On MinGW with internal libraries, link the standard library statically
@ -224,8 +259,19 @@ if(("${CMAKE_COMPILER_IS_GNUCC}" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") AND
endif()
endif()
if(MSVC OR CMAKE_CXX_LINK_EXECUTABLE MATCHES "lld-link.exe")
if(MSVC OR CMAKE_CXX_LINK_EXECUTABLE MATCHES "lld-link\\.exe")
target_link_options(SRB2SDL2 PRIVATE /MANIFEST:NO)
target_link_libraries(SRB2SDL2 PRIVATE "dbghelp")
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND MSVC)
# Stuff to make paths not include the builder's file system paths on clang-cl
string(REPLACE "/" "\\" backslashes_cmake_source_dir "${CMAKE_SOURCE_DIR}")
target_compile_options(SRB2SDL2 PRIVATE "$<$<CONFIG:RelWithDebInfo,Release>:/clang:-fdebug-prefix-map=${CMAKE_SOURCE_DIR}=X:/ringracers>")
target_compile_options(SRB2SDL2 PRIVATE "$<$<CONFIG:RelWithDebInfo,Release>:/clang:-fdebug-prefix-map=${backslashes_cmake_source_dir}=X:/ringracers>")
target_compile_options(SRB2SDL2 PRIVATE "$<$<CONFIG:RelWithDebInfo,Release>:/clang:-fdebug-compilation-dir=${CMAKE_SOURCE_DIR}>")
target_link_options(SRB2SDL2 PRIVATE "$<$<CONFIG:RelWithDebInfo,Release>:/pdbaltpath:%_PDB%>")
endif()
target_compile_features(SRB2SDL2 PRIVATE c_std_11 cxx_std_17)
@ -283,6 +329,10 @@ target_link_libraries(SRB2SDL2 PRIVATE ZLIB::ZLIB)
target_link_libraries(SRB2SDL2 PRIVATE PNG::PNG)
target_link_libraries(SRB2SDL2 PRIVATE CURL::libcurl)
target_link_libraries(SRB2SDL2 PRIVATE Opus::opus)
if(WIN32 AND NOT MINGW)
target_link_libraries(SRB2SDL2 PRIVATE cpptrace::cpptrace)
target_compile_definitions(SRB2SDL2 PRIVATE HAVE_CPPTRACE)
endif()
target_link_libraries(SRB2SDL2 PRIVATE ReNameNoise::renamenoise)
if("${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD")
target_link_libraries(SRB2SDL2 PRIVATE -lexecinfo)
@ -461,6 +511,7 @@ target_compile_options(SRB2SDL2 PRIVATE
# This is a direct translation from versions.mk
$<$<AND:$<COMPILE_LANGUAGE:C>,$<C_COMPILER_ID:GNU>>:
-Wall
-Werror-implicit-function-declaration
-Wno-trigraphs
-W # Was controlled by RELAXWARNINGS
-pedantic
@ -545,6 +596,7 @@ target_compile_options(SRB2SDL2 PRIVATE
$<$<AND:$<COMPILE_LANGUAGE:C>,$<OR:$<C_COMPILER_ID:AppleClang>,$<C_COMPILER_ID:Clang>>>:
#-Wall
-Werror=microsoft
-Werror=implicit-function-declaration
-Wreturn-type # Missing returns in non-void function
-Wduplicate-decl-specifier
-Wsometimes-uninitialized
@ -570,9 +622,13 @@ target_compile_options(SRB2SDL2 PRIVATE
# C++, GNU
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:GNU>>:
-Wall
-Wno-c++11-compat
-Wno-c++14-compat # No C++14 compat needed
-Wc++20-extensions
-Wc++23-extensions # Disallow newer language features entirely
>
# C++, GNU, Clang and Apple Clang
# C++, Clang and Apple Clang
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<OR:$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:Clang>>>:
#-Wall
-Werror=microsoft
@ -584,6 +640,8 @@ target_compile_options(SRB2SDL2 PRIVATE
-Wno-c++98-compat
-Wno-c++11-compat
-Wno-c++14-compat # No C++14 compat needed
-Werror=c++20-extensions
-Werror=c++23-extensions # Disallow newer language features entirely
-Wno-unused-but-set-variable # Setting unread variables is fine (nontrivial C++ types issue)
-Wno-misleading-indentation # Some cases in code currently
-Wno-deprecated-non-prototype # We have no intention of using C23 yet.
@ -594,6 +652,8 @@ target_compile_options(SRB2SDL2 PRIVATE
-Wno-unused-function
>
# C++, Clang and Apple Clang
# C++, MSVC
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:MSVC>>:
/Wv:19.20.27004.0

View file

@ -25,6 +25,7 @@
#include "Script.hpp"
#include "Serial.hpp"
#include "Thread.hpp"
#include "../../../w_wad.h"
#include <iostream>
#include <list>
@ -596,7 +597,12 @@ namespace ACSVM
ModuleName Environment::readModuleName(Serial &in) const
{
auto s = readString(in);
auto i = ReadVLN<std::size_t>(in);
size_t i = ReadVLN<std::size_t>(in);
if ((i = W_LumpFromNetSave(i)) == LUMPERROR)
{
CONS_Debug(DBG_GAMELOGIC, "lumpnum not found for ACS module '%s'\n", s->str);
}
return {s, nullptr, i};
}
@ -768,7 +774,7 @@ namespace ACSVM
void Environment::writeModuleName(Serial &out, ModuleName const &in) const
{
writeString(out, in.s);
WriteVLN(out, in.i);
WriteVLN<std::size_t>(out, W_LumpIntoNetSave(in.i));
}
//

View file

@ -829,6 +829,7 @@ void ForceSkin_OnChange(void);
consvar_t cv_forceskin = OnlineCheat("forcecharacter", "None").onchange(ForceSkin_OnChange).description("Force all players to use one character");
consvar_t cv_fuzz = OnlineCheat("fuzz", "Off").on_off().description("Human players spam random inputs, get random items");
consvar_t cv_skiprender = OnlineCheat("skiprender", "1").values(CV_Natural).description("Only render every X frames");
consvar_t cv_kartdebugamount = OnlineCheat("debugitemamount", "1").min_max(1, 255).description("If debugitem, give multiple copies of an item");
consvar_t cv_kartdebugbots = OnlineCheat("debugbots", "Off").on_off().description("Bot AI debugger");

View file

@ -206,8 +206,11 @@ static UINT32 g_player_voice_frames_this_tic[MAXPLAYERS];
static OpusEncoder *g_local_opus_encoder;
static ReNameNoiseDenoiseState *g_local_renamenoise_state;
static UINT64 g_local_opus_frame = 0;
#define SRB2_VOICE_OPUS_FRAME_SIZE 960
static float g_local_voice_buffer[SRB2_VOICE_OPUS_FRAME_SIZE];
#define SRB2_VOICE_OPUS_FRAME_SIZE (20 * 48)
#define SRB2_VOICE_MAX_FRAMES 8
#define SRB2_VOICE_MAX_DEQUEUE_SAMPLES (SRB2_VOICE_MAX_FRAMES * SRB2_VOICE_OPUS_FRAME_SIZE)
#define SRB2_VOICE_MAX_DEQUEUE_BYTES (SRB2_VOICE_MAX_DEQUEUE_SAMPLES * sizeof(float))
static float g_local_voice_buffer[SRB2_VOICE_MAX_DEQUEUE_SAMPLES];
static INT32 g_local_voice_buffer_len = 0;
static INT32 g_local_voice_threshold_time = 0;
float g_local_voice_last_peak = 0;
@ -3742,8 +3745,6 @@ static void InitializeLocalVoiceEncoder(void)
CONS_Alert(CONS_WARNING, "Failed to create Opus voice encoder: opus error %d\n", error);
encoder = NULL;
}
opus_encoder_ctl(encoder, OPUS_SET_VBR(0));
opus_encoder_ctl(encoder, OPUS_SET_BITRATE(28000));
g_local_opus_encoder = encoder;
g_local_opus_frame = 0;
}
@ -3811,6 +3812,12 @@ static void Got_AddPlayer(const UINT8 **p, INT32 playernum)
newplayer->availabilities[i] = READUINT8(*p);
}
if (server)
{
for (i = 0; i < G_LocalSplitscreenPartySize(newplayernum); ++i)
playerdelaytable[G_LocalSplitscreenPartyMember(newplayernum, i)] = mindelay;
}
// the server is creating my player
if (node == mynode)
{
@ -3838,12 +3845,6 @@ static void Got_AddPlayer(const UINT8 **p, INT32 playernum)
P_ForceLocalAngle(newplayer, newplayer->angleturn);
addedtogame = true;
if (server)
{
for (i = 0; i < G_LocalSplitscreenPartySize(newplayernum); ++i)
playerdelaytable[G_LocalSplitscreenPartyMember(newplayernum, i)] = mindelay;
}
}
newplayer->splitscreenindex = splitscreenplayer;
@ -5379,7 +5380,7 @@ static void PT_HandleVoiceClient(SINT8 node, boolean isserver)
{
return;
}
float *decoded_out = Z_Malloc(sizeof(float) * SRB2_VOICE_OPUS_FRAME_SIZE, PU_STATIC, NULL);
float *decoded_out = Z_Malloc(sizeof(float) * 1920, PU_STATIC, NULL);
INT32 decoded_samples = 0;
UINT64 missedframes = 0;
@ -5390,7 +5391,7 @@ static void PT_HandleVoiceClient(SINT8 node, boolean isserver)
for (UINT64 i = 0; i < missedframes; i++)
{
decoded_samples = opus_decode_float(decoder, NULL, 0, decoded_out, SRB2_VOICE_OPUS_FRAME_SIZE, 0);
decoded_samples = opus_decode_float(decoder, NULL, 0, decoded_out, 1920, 0);
if (decoded_samples < 0)
{
continue;
@ -5402,7 +5403,7 @@ static void PT_HandleVoiceClient(SINT8 node, boolean isserver)
}
g_player_opus_lastframe[playernum] = framenum;
decoded_samples = opus_decode_float(decoder, frame, framesize, decoded_out, SRB2_VOICE_OPUS_FRAME_SIZE, 0);
decoded_samples = opus_decode_float(decoder, frame, framesize, decoded_out, 1920, 0);
if (decoded_samples < 0)
{
Z_Free(decoded_out);
@ -7566,9 +7567,19 @@ void NetUpdate(void)
FileSendTicker();
}
static INT32 BiggestOpusFrameLength(INT32 samples)
{
if (samples >= 1920) return 1920;
if (samples >= 960) return 960;
if (samples >= 480) return 480;
return 0;
}
void NetVoiceUpdate(void)
{
UINT8 *encoded = NULL;
float *subframe_buffer = NULL;
float *denoise_buffer = NULL;
if (dedicated)
{
@ -7576,42 +7587,25 @@ void NetVoiceUpdate(void)
}
UINT32 bytes_dequed = 0;
do
bytes_dequed = S_SoundInputDequeueSamples((void*)(g_local_voice_buffer + g_local_voice_buffer_len), SRB2_VOICE_MAX_DEQUEUE_BYTES - (g_local_voice_buffer_len * sizeof(float)));
g_local_voice_buffer_len += bytes_dequed / 4;
INT32 buffer_offset = 0;
INT32 frame_length = 0;
for (
;
(frame_length = BiggestOpusFrameLength(g_local_voice_buffer_len - buffer_offset)) > 0 && (buffer_offset + frame_length) < g_local_voice_buffer_len;
buffer_offset += frame_length
)
{
// We need to drain the input queue completely, so do this in a full loop
UINT32 to_read = (SRB2_VOICE_OPUS_FRAME_SIZE - g_local_voice_buffer_len) * sizeof(float);
if (to_read > 0)
{
// Attempt to fill the voice frame buffer
bytes_dequed = S_SoundInputDequeueSamples((void*)(g_local_voice_buffer + g_local_voice_buffer_len), to_read);
g_local_voice_buffer_len += bytes_dequed / 4;
}
else
{
bytes_dequed = 0;
}
if (g_local_voice_buffer_len < SRB2_VOICE_OPUS_FRAME_SIZE)
{
continue;
}
if (S_SoundInputRemainingSamples() > 5 * SRB2_VOICE_OPUS_FRAME_SIZE)
{
// If there are too many frames worth of samples to dequeue (100ms), skip this frame instead of encoding.
// This is so we drain the queue without sending too many packets that might queue up on the network driver.
g_local_voice_buffer_len = 0;
continue;
}
float *frame_buffer = g_local_voice_buffer + buffer_offset;
// Amp of +10 dB is appromiately "twice as loud"
float ampfactor = powf(10, (float) cv_voice_inputamp.value / 20.f);
for (int i = 0; i < g_local_voice_buffer_len; i++)
for (int i = 0; i < frame_length; i++)
{
g_local_voice_buffer[i] *= ampfactor;
frame_buffer[i] *= ampfactor;
}
if (cv_voice_denoise.value)
@ -7620,30 +7614,34 @@ void NetVoiceUpdate(void)
{
InitializeLocalVoiceDenoiser();
}
int rnnoise_size = renamenoise_get_frame_size();
float *subframe_buffer = (float*) Z_Malloc(rnnoise_size * sizeof(float), PU_STATIC, NULL);
float *denoise_buffer = (float*) Z_Malloc(rnnoise_size * sizeof(float), PU_STATIC, NULL);
int rnnoise_size = renamenoise_get_frame_size(); // this is always 480
if (subframe_buffer == NULL)
{
subframe_buffer = (float*) Z_Malloc(rnnoise_size * sizeof(float), PU_STATIC, NULL);
}
if (denoise_buffer == NULL)
{
denoise_buffer = (float*) Z_Malloc(rnnoise_size * sizeof(float), PU_STATIC, NULL);
}
// rnnoise frames are smaller than opus, but we should not expect the opus frame to be an exact multiple of rnnoise
for (int denoise_position = 0; denoise_position < SRB2_VOICE_OPUS_FRAME_SIZE; denoise_position += rnnoise_size)
for (int denoise_position = 0; denoise_position < frame_length; denoise_position += rnnoise_size)
{
memset(subframe_buffer, 0, rnnoise_size * sizeof(float));
memcpy(subframe_buffer, g_local_voice_buffer + denoise_position, min(rnnoise_size * sizeof(float), (SRB2_VOICE_OPUS_FRAME_SIZE - denoise_position) * sizeof(float)));
memcpy(subframe_buffer, frame_buffer + denoise_position, min(rnnoise_size * sizeof(float), (frame_length - denoise_position) * sizeof(float)));
renamenoise_process_frame(g_local_renamenoise_state, denoise_buffer, subframe_buffer);
memcpy(g_local_voice_buffer + denoise_position, denoise_buffer, min(rnnoise_size * sizeof(float), (SRB2_VOICE_OPUS_FRAME_SIZE - denoise_position) * sizeof(float)));
memcpy(frame_buffer + denoise_position, denoise_buffer, min(rnnoise_size * sizeof(float), (frame_length - denoise_position) * sizeof(float)));
}
Z_Free(denoise_buffer);
Z_Free(subframe_buffer);
}
float softmem = 0.f;
opus_pcm_soft_clip(g_local_voice_buffer, SRB2_VOICE_OPUS_FRAME_SIZE, 1, &softmem);
opus_pcm_soft_clip(frame_buffer, frame_length, 1, &softmem);
// Voice detection gate open/close
float maxamplitude = 0.f;
for (int i = 0; i < g_local_voice_buffer_len; i++)
for (int i = 0; i < frame_length; i++)
{
maxamplitude = max(fabsf(g_local_voice_buffer[i]), maxamplitude);
maxamplitude = max(fabsf(frame_buffer[i]), maxamplitude);
}
// 20. * log_10(amplitude) -> decibels (up to 0)
// lower than -30 dB is usually inaudible
@ -7660,7 +7658,6 @@ void NetVoiceUpdate(void)
case 0:
if (I_GetTime() - g_local_voice_threshold_time > 15)
{
g_local_voice_buffer_len = 0;
g_local_voice_detected = false;
continue;
}
@ -7668,20 +7665,17 @@ void NetVoiceUpdate(void)
case 1:
if (!g_voicepushtotalk_on)
{
g_local_voice_buffer_len = 0;
g_local_voice_detected = false;
continue;
}
g_local_voice_detected = true;
break;
default:
g_local_voice_buffer_len = 0;
continue;
}
if (cv_voice_selfdeafen.value == 1)
{
g_local_voice_buffer_len = 0;
continue;
}
@ -7696,7 +7690,7 @@ void NetVoiceUpdate(void)
}
OpusEncoder *encoder = g_local_opus_encoder;
INT32 result = opus_encode_float(encoder, g_local_voice_buffer, SRB2_VOICE_OPUS_FRAME_SIZE, encoded, 1400);
INT32 result = opus_encode_float(encoder, frame_buffer, frame_length, encoded, 1400);
if (result < 0)
{
continue;
@ -7718,14 +7712,20 @@ void NetVoiceUpdate(void)
{
RecreatePlayerOpusDecoder(consoleplayer);
}
result = opus_decode_float(g_player_opus_decoders[consoleplayer], encoded, result, g_local_voice_buffer, SRB2_VOICE_OPUS_FRAME_SIZE, 0);
S_QueueVoiceFrameFromPlayer(consoleplayer, g_local_voice_buffer, result * sizeof(float), false);
result = opus_decode_float(g_player_opus_decoders[consoleplayer], encoded, result, frame_buffer, frame_length, 0);
S_QueueVoiceFrameFromPlayer(consoleplayer, frame_buffer, result * sizeof(float), false);
}
g_local_voice_buffer_len = 0;
g_local_opus_frame += 1;
} while (bytes_dequed > 0);
}
if (buffer_offset > 0)
{
memmove(g_local_voice_buffer, g_local_voice_buffer + buffer_offset, (g_local_voice_buffer_len - buffer_offset) * sizeof(float));
g_local_voice_buffer_len -= buffer_offset;
}
if (denoise_buffer) Z_Free(denoise_buffer);
if (subframe_buffer) Z_Free(subframe_buffer);
if (encoded) Z_Free(encoded);
return;
}

View file

@ -107,17 +107,17 @@ extern "C" consvar_t cv_lua_profile, cv_menuframeskip;
/* Manually defined asset hashes
*/
#define ASSET_HASH_BIOS_PK3 "70ad11d77048078ee0adb7891068bf38"
#define ASSET_HASH_BIOS_PK3 "5f9093a5c6abfb77ab518ac530312564"
#define ASSET_HASH_SCRIPTS_PK3 "c3440a9ee57b4d9a81145a09afa0c0d6"
#define ASSET_HASH_GFX_PK3 "314e88e73c0a629df9c2851dd7c21baf"
#define ASSET_HASH_GFX_PK3 "24a59ebaa74f253dbec55b00328accb9"
#define ASSET_HASH_TEXTURES_GENERAL_PK3 "609b683d3efc291ea28dd4e50d731f34"
#define ASSET_HASH_TEXTURES_SEGAZONES_PK3 "61a19cb324e66b84e0fbc07abb659c64"
#define ASSET_HASH_TEXTURES_SEGAZONES_PK3 "7ef635c2a5495dad031acf5f0b090045"
#define ASSET_HASH_TEXTURES_ORIGINALZONES_PK3 "2f3aa120be2dfb1f4fe3e7090fbb0948"
#define ASSET_HASH_CHARS_PK3 "5c8c34c5623acf984e3f654da4509126"
#define ASSET_HASH_FOLLOWERS_PK3 "4b61428e5f2ec806de398de8a5fba5f0"
#define ASSET_HASH_MAPS_PK3 "84503a914248842b3e88db4f1080b8e6"
#define ASSET_HASH_MAPS_PK3 "b5af5f4f8bb4380dbf87f91014fca028"
#define ASSET_HASH_UNLOCKS_PK3 "a4de35ba9f83829ced44dfc1316ba33e"
#define ASSET_HASH_STAFFGHOSTS_PK3 "f2ea75218c9b8ef479a75f287cc32d11"
#define ASSET_HASH_STAFFGHOSTS_PK3 "04a6ac1e67c95fa356aac8b505749a4a"
#define ASSET_HASH_SHADERS_PK3 "bc0b47744d457956db2ee9ea00f59eff"
#ifdef USE_PATCH_FILE
#define ASSET_HASH_PATCH_PK3 "00000000000000000000000000000000"
@ -843,6 +843,8 @@ static bool D_Display(bool world)
tic_t rendergametic;
extern "C" consvar_t cv_skiprender;
void D_SRB2Loop(void)
{
tic_t entertic = 0, oldentertics = 0, realtics = 0, rendertimeout = INFTICS;
@ -1123,6 +1125,13 @@ void D_SRB2Loop(void)
else
frameskip = 0;
}
else if (cv_skiprender.value > 1)
{
if (frameskip < cv_skiprender.value)
frameskip++;
else
frameskip = 0;
}
else
{
if (!ranwipe && frameskip < 3 && deltatics > 1.0)

View file

@ -4639,10 +4639,10 @@ static void Command_ListWADS_f(void)
nameonly(tempname = va("%s", wadfiles[i]->filename));
if (!i)
CONS_Printf("\x82 IWAD\x80: %s\n", tempname);
else if (i <= mainwads)
else if (i < mainwads)
CONS_Printf("\x82 * %.2d\x80: %s\n", i, tempname);
else if (!wadfiles[i]->important)
CONS_Printf("\x86 %c %.2d: %s\n", ((i <= mainwads + musicwads) ? '*' : ' '), i, tempname);
CONS_Printf("\x86 %c %.2d: %s\n", ((i < mainwads + musicwads) ? '*' : ' '), i, tempname);
else
CONS_Printf(" %.2d: %s\n", i, tempname);
}
@ -6876,6 +6876,7 @@ static void Command_Staffsync(void)
demostarttime = I_GetTime();
staffbrief = mapheader->ghostBrief[staffsync_ghost];
G_DoPlayDemoEx("", (staffbrief->wad << 16) | staffbrief->lump);
staffsync_ghost++;
@ -6923,6 +6924,28 @@ static void Command_Staffsync(void)
CONS_Printf("\n");
CONS_Printf(" %d syncs (%d error)\n", result->numerror, result->totalerror/FRACUNIT);
CONS_Printf(" presync: ");
for (UINT32 j = 0; j < PRNUMSYNCED; j++)
{
if (result->rngerror_presync[j] > 0)
CONS_Printf("%s %d ", rng_class_names[j], result->rngerror_presync[j]);
}
CONS_Printf("\n");
CONS_Printf(" postsync: ");
for (UINT32 j = 0; j < PRNUMSYNCED; j++)
{
if (result->rngerror_postsync[j] > 0)
CONS_Printf("%s %d ", rng_class_names[j], result->rngerror_postsync[j]);
}
CONS_Printf("\n");
i++;
}

View file

@ -163,10 +163,10 @@ UINT8 *PutFileNeeded(UINT16 firstfile)
#ifdef DEVELOP
i = 0;
#else
i = mainwads + 1;
i = mainwads + musicwads;
#endif
for (; i < numwadfiles; i++) //mainwads+1, otherwise we start on the first mainwad
for (; i < numwadfiles; i++) //mainwads+musicwads, otherwise we start on the first mainwad
{
// If it has only music/sound lumps, don't put it in the list
if (!wadfiles[i]->important)
@ -197,7 +197,7 @@ UINT8 *PutFileNeeded(UINT16 firstfile)
/* don't send mainwads!! */
#ifdef DEVELOP
if (i <= mainwads)
if (i < mainwads)
filestatus += (2 << 4);
#endif
@ -565,7 +565,7 @@ INT32 CL_CheckFiles(void)
#ifdef DEVELOP
j = 0;
#else
j = mainwads + 1;
j = mainwads + musicwads;
#endif
for (i = 0; i < fileneedednum || j < numwadfiles;)
{

View file

@ -383,7 +383,7 @@ typedef enum
#define TUMBLEBOUNCES 3
#define TUMBLEGRAVITY (4*FRACUNIT)
#define TRIPWIRETIME (15)
#define TRIPWIRETIME (50)
#define BALLHOGINCREMENT (7)
@ -801,6 +801,8 @@ struct player_t
fixed_t spindashspeed; // Spindash release speed
UINT8 spindashboost; // Spindash release boost timer
UINT8 ringboostinprogress; // Ring overhead, don't sting!
fixed_t fastfall; // Fast fall momentum
fixed_t fastfallBase; // Fast fall base speed multiplier
@ -821,6 +823,7 @@ struct player_t
UINT16 tripwireLeniency; // When reaching a state that lets you go thru tripwire, you get an extra second leniency after it ends to still go through it.
UINT8 tripwireAirLeniency; // Timer that elongates tripwire leniency when in midair.
UINT8 fakeBoost; // Some items need to grant tripwire pass briefly, even when their effect is thrust/instathrust. This is a fake boost type to control that.
UINT16 subsonicleniency; // Keep the subsonic visual for just a little bit when your sonic boom is visible
itemroulette_t itemRoulette; // Item roulette data
@ -1071,8 +1074,6 @@ struct player_t
UINT16 wavedashboost; // The actual boost granted from wavedash.
fixed_t wavedashpower; // Is this a bullshit "tap" wavedash? Weaken lower-charge wavedashes while keeping long sliptides fully rewarding.
UINT16 speedpunt;
UINT16 trickcharge; // Landed normally from a trick panel? Get the benefits package!
UINT16 infinitether; // Generic infinitether time, used for infinitether leniency.
@ -1128,6 +1129,8 @@ struct player_t
boolean dotrickfx;
boolean stingfx;
UINT8 bumperinflate;
boolean mfdfinish; // Did you cross the finish line while just about to explode?
UINT8 ringboxdelay; // Delay until Ring Box auto-activates
UINT8 ringboxaward; // Where did we stop?

View file

@ -1599,6 +1599,8 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_AMPBURST",
// Tripwire VFX on player for bumping it or passing it
"S_SONICBOOM",
"S_TRIPWIREOK",
"S_TRIPWIRELOCKOUT",
@ -3597,6 +3599,7 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_AMPAURA",
"MT_AMPBURST",
"MT_SONICBOOM",
"MT_TRIPWIREOK",
"MT_TRIPWIRELOCKOUT",

View file

@ -457,7 +457,7 @@ enum {
*/
void I_Error(const char *error, ...) FUNCIERROR;
FUNCIERROR void ATTRNORETURN I_Error(const char *error, ...);
/** \brief write a message to stderr (use before I_Quit) for when you need to quit with a msg, but need
the return code 0 of I_Quit();
@ -757,8 +757,8 @@ extern int
#define EXP_STABLERATE 3*FRACUNIT/10 // how low is your placement before losing XP? 4*FRACUNIT/10 = top 40% of race will gain
#define EXP_POWER 3*FRACUNIT/100 // adjust to change overall xp volatility
#define EXP_MIN 25 // The min value target
#define EXP_TARGET 120 // Used for grading ...
#define EXP_MAX 120 // The max value displayed by the hud and in the tally screen and GP results screen
#define EXP_TARGET 150 // Used for grading ...
#define EXP_MAX 150 // The max value displayed by the hud and in the tally screen and GP results screen
#ifdef __cplusplus
} // extern "C"

View file

@ -245,6 +245,10 @@ struct staffsync_t
char name[MAXPLAYERNAME+1];
UINT32 reason;
UINT32 extra;
fixed_t totalerror;
UINT32 numerror;
UINT32 rngerror_presync[32];
UINT32 rngerror_postsync[32];
};
extern staffsync_t staffsync_results[1024];

View file

@ -1593,6 +1593,7 @@ void F_StartGameEnd(void)
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
F_WipeEndScreen();
F_RunWipe(wipe_level_toblack, wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false);
Music_Stop("credits");
nextmap = NEXTMAP_TITLE;
G_EndGame();

View file

@ -303,18 +303,39 @@ boolean G_ConsiderEndingDemoRead(void)
}
// Demo failed sync during a sync test! Log the failure to be reported later.
static void G_FailStaffSync(staffsync_reason_t reason, UINT32 extra)
static boolean G_FailStaffSync(staffsync_reason_t reason, UINT32 extra)
{
if (demo.attract != DEMO_ATTRACT_OFF) // Don't shout about RNG desyncs in titledemos
return false;
if (!staffsync)
return;
return true;
if (staffsync_results[staffsync_failed].reason != 0)
return;
return false;
if (reason == SYNC_RNG)
{
switch (extra)
{
case PR_ITEM_DEBRIS:
case PR_RANDOMAUDIENCE:
case PR_VOICES:
case PR_DECORATION:
case PR_RANDOMANIM:
CONS_Printf("[!] Ignored desync from RNG class %d\n", extra);
return false;
default:
break;
}
}
staffsync_results[staffsync_failed].map = gamemap;
memcpy(&staffsync_results[staffsync_failed].name, player_names[consoleplayer], sizeof(player_names[consoleplayer]));
staffsync_results[staffsync_failed].reason = reason;
staffsync_results[staffsync_failed].extra = extra;
return true;
}
void G_ReadDemoExtraData(void)
@ -484,13 +505,26 @@ void G_ReadDemoExtraData(void)
{
P_SetRandSeed(static_cast<pr_class_t>(i), rng);
if (demosynced)
if (staffsync)
{
CONS_Alert(CONS_WARNING, "Demo playback has desynced (RNG class %d)!\n", i);
G_FailStaffSync(SYNC_RNG, i);
if (demosynced)
staffsync_results[staffsync_failed].rngerror_presync[i]++;
else
staffsync_results[staffsync_failed].rngerror_postsync[i]++;
}
storesynced = false;
if (demosynced)
{
if (G_FailStaffSync(SYNC_RNG, i))
{
CONS_Alert(CONS_WARNING, "Demo playback has desynced (RNG class %d - %s)!\n", i, rng_class_names[i]);
storesynced = false;
}
}
else
{
storesynced = false;
}
}
}
demosynced = storesynced;
@ -1242,6 +1276,12 @@ void G_ConsGhostTic(INT32 playernum)
}
demosynced = false;
if (staffsync)
{
staffsync_results[staffsync_failed].numerror++;
staffsync_results[staffsync_failed].totalerror += abs(testmo->x - oldghost[playernum].x) + abs(testmo->y - oldghost[playernum].y) + abs(testmo->z - oldghost[playernum].z);
}
P_UnsetThingPosition(testmo);
testmo->x = oldghost[playernum].x;
testmo->y = oldghost[playernum].y;
@ -3269,6 +3309,8 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum)
// net var data
demobuf.p += CV_LoadDemoVars(demobuf.p);
// Dumb hack for team play desyncs - https://gitlab.com/kart-krew-dev/ring-racers/-/issues/210
g_teamplay = cv_teamplay.value ? 1 : 0;
memset(&grandprixinfo, 0, sizeof grandprixinfo);
if ((demoflags & DF_GRANDPRIX))

View file

@ -1885,7 +1885,7 @@ void G_Ticker(boolean run)
P_MapStart();
if (demo.playback && staffsync && !demosynced)
if (demo.playback && staffsync && !demosynced && false) // We want to assess the magnitude of position desync, don't bail early!
{
G_ClearRetryFlag();
G_StopDemo();
@ -2312,6 +2312,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
UINT16 bigwaypointgap;
INT16 duelscore;
boolean mfdfinish;
roundconditions_t roundconditions;
boolean saveroundconditions;
@ -2406,6 +2408,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
totalring = players[player].totalring;
xtralife = players[player].xtralife;
mfdfinish = players[player].mfdfinish;
pflags = (players[player].pflags & (PF_WANTSTOJOIN|PF_KICKSTARTACCEL|PF_SHRINKME|PF_SHRINKACTIVE|PF_AUTOROULETTE|PF_ANALOGSTICK|PF_AUTORING));
pflags2 = (players[player].pflags2 & (PF2_SELFMUTE | PF2_SELFDEAFEN | PF2_SERVERTEMPMUTE | PF2_SERVERMUTE | PF2_SERVERDEAFEN | PF2_STRICTFASTFALL));
@ -2483,6 +2487,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
lastsafecheatcheck = 0;
bigwaypointgap = 0;
duelscore = 0;
mfdfinish = 0;
finalized = false;
@ -2675,6 +2680,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
p->xtralife = xtralife;
p->finalized = finalized;
p->mfdfinish = mfdfinish;
// SRB2kart
p->itemtype = itemtype;

View file

@ -92,7 +92,7 @@ void I_OsPolling(void);
/** \brief Called by M_Responder when quit is selected, return exit code 0
*/
void I_Quit(void) FUNCNORETURN;
FUNCNORETURN void ATTRNORETURN I_Quit(void);
typedef enum
{

View file

@ -614,6 +614,7 @@ char sprnames[NUMSPRITES + 1][5] =
"EXPC",
"TWBB",
"TWOK",
"TW_L",
@ -2225,6 +2226,7 @@ state_t states[NUMSTATES] =
{SPR_AMPD, FF_FULLBRIGHT|FF_ANIMATE|0, -1, {NULL}, 4, 2, S_NULL}, // S_AMPAURA
{SPR_AMPB, FF_FULLBRIGHT|FF_ADD|FF_PAPERSPRITE|2, -1, {NULL}, 4, 2, S_NULL}, // S_AMPBURST
{SPR_TWBB, FF_ADD|FF_PAPERSPRITE|0, -1, {NULL}, 0, 0, S_NULL}, // S_SONICBOOM
{SPR_TWOK, FF_FULLBRIGHT|FF_ANIMATE|0, 56, {NULL}, 55, 1, S_NULL}, // S_TRIPWIREOK
{SPR_TW_L, FF_FULLBRIGHT|FF_ANIMATE|0, 56, {NULL}, 55, 1, S_NULL}, // S_TRIPWIRELOCKOUT
@ -13998,6 +14000,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
{ // MT_SONICBOOM
-1, // doomednum
S_SONICBOOM, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
67*FRACUNIT, // radius
67*FRACUNIT, // height
1, // display offset
100, // mass
0, // damage
sfx_None, // activesound
MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
S_NULL // raisestate
},
{ // MT_TRIPWIREOK
-1, // doomednum
S_TRIPWIREOK, // spawnstate

View file

@ -1155,6 +1155,7 @@ typedef enum sprite
SPR_EXPC,
SPR_TWBB, // Sonic Boom
SPR_TWOK, // Tripwire OK
SPR_TW_L, // Tripwire Lockout
@ -2681,6 +2682,7 @@ typedef enum state
S_AMPAURA,
S_AMPBURST,
S_SONICBOOM,
S_TRIPWIREOK,
S_TRIPWIRELOCKOUT,
@ -4706,6 +4708,7 @@ typedef enum mobj_type
MT_AMPAURA,
MT_AMPBURST,
MT_SONICBOOM,
MT_TRIPWIREOK,
MT_TRIPWIRELOCKOUT,

View file

@ -133,8 +133,12 @@ void K_CheckBumpers(void)
{
UINT8 i;
UINT8 numingame = 0;
UINT8 rednumingame = 0;
UINT8 bluenumingame = 0;
UINT8 nobumpers = 0;
UINT8 eliminated = 0;
UINT8 redeliminated = 0;
UINT8 blueeliminated = 0;
SINT8 kingofthehill = -1;
if (!(gametyperules & GTR_BUMPERS))
@ -143,6 +147,8 @@ void K_CheckBumpers(void)
if (gameaction == ga_completed)
return;
boolean team = G_GametypeHasTeams();
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator) // not even in-game
@ -152,6 +158,11 @@ void K_CheckBumpers(void)
return;
numingame++;
if (team)
{
if (players[i].team == 1) rednumingame++;
if (players[i].team == 2) bluenumingame++;
}
if (!P_MobjWasRemoved(players[i].mo) && players[i].mo->health <= 0) // if you don't have any bumpers, you're probably not a winner
{
@ -161,6 +172,11 @@ void K_CheckBumpers(void)
if (players[i].pflags & PF_ELIMINATED)
{
eliminated++;
if (team)
{
if (players[i].team == 1) redeliminated++;
else if (players[i].team == 2) blueeliminated++;
}
}
else
{
@ -168,6 +184,12 @@ void K_CheckBumpers(void)
}
}
boolean teamwin = false;
if (team && (rednumingame - redeliminated == 0 || bluenumingame - blueeliminated == 0))
{
teamwin = true;
}
if (numingame - eliminated == 2 && battleovertime.enabled && battleovertime.radius <= BARRIER_MIN_RADIUS)
{
Music_Stop("battle_overtime");
@ -186,9 +208,28 @@ void K_CheckBumpers(void)
{
// If every other player is eliminated, the
// last player standing wins by default.
if (eliminated >= numingame - 1)
// Or, if an entire team is eliminated.
if (eliminated >= numingame - 1 || teamwin)
{
K_EndBattleRound(kingofthehill != -1 ? &players[kingofthehill] : NULL);
if (teamwin)
{
// Find the player with the highest individual score
UINT32 highestscore = 0;
UINT32 highestplayer = 0;
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && players[i].score > highestscore)
{
highestplayer = i;
highestscore = players[i].score;
}
}
K_EndBattleRound(&players[highestplayer]);
}
else
{
K_EndBattleRound(kingofthehill != -1 ? &players[kingofthehill] : NULL);
}
return;
}
}

View file

@ -1034,10 +1034,6 @@ boolean K_InstaWhipCollide(mobj_t *shield, mobj_t *victim)
}
// if you're here, you're getting hit
// Damage is a bit hacky, we want only a small loss-of-control
// while still behaving as if it's a "real" hit.
P_PlayRinglossSound(victim);
P_PlayerRingBurst(victimPlayer, 5);
P_DamageMobj(victim, shield, attacker, 1, DMG_WHUMBLE);
K_DropPowerUps(victimPlayer);
@ -1293,8 +1289,15 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2)
auto doStumble = [](mobj_t *t1, mobj_t *t2)
{
K_StumblePlayer(t2->player);
K_SpawnAmps(t1->player, K_PvPAmpReward(20, t1->player, t2->player), t2);
if (gametyperules & GTR_BUMPERS)
{
K_StumblePlayer(t2->player);
K_SpawnAmps(t1->player, K_PvPAmpReward(20, t1->player, t2->player), t2);
}
else
{
P_DamageMobj(t2, t1, t1, 1, DMG_WHUMBLE);
}
};
if (forEither(shouldStumble, doStumble))
@ -1325,7 +1328,7 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2)
bool stung = false;
if (RINGTOTAL(t2->player) <= 0 && t2->health == 1 && !(t2->player->pflags2 & PF2_UNSTINGABLE))
if (RINGTOTAL(t2->player) <= 0 && t2->player->ringboostinprogress == 0 && t2->health == 1 && !(t2->player->pflags2 & PF2_UNSTINGABLE))
{
P_DamageMobj(t2, t1, t1, 1, DMG_STING|DMG_WOMBO);
// CONS_Printf("T2 stung\n");

View file

@ -22,6 +22,26 @@
#include "s_sound.h"
#include "m_easing.h"
// Use for adding hitlag that should be mostly ignored by impervious players.
// (Currently only called in power clash, but in the future...?)
void K_AddHitLagFromCollision(mobj_t *mo, INT32 tics)
{
boolean doAnything = true;
if (mo->player == NULL || mo->type != MT_PLAYER)
doAnything = false;
else if (!K_PlayerCanPunt(mo->player))
doAnything = false;
if (!doAnything)
{
K_AddHitLag(mo, tics, false);
return;
}
K_AddHitLag(mo, min(tics, 2), false);
}
/*--------------------------------------------------
void K_AddHitLag(mobj_t *mo, INT32 tics, boolean fromDamage)
@ -34,9 +54,6 @@ void K_AddHitLag(mobj_t *mo, INT32 tics, boolean fromDamage)
return;
}
if (mo->player && mo->player->overshield)
tics = min(tics, 3);
mo->hitlag += tics;
mo->hitlag = min(mo->hitlag, MAXHITLAGTICS);

View file

@ -26,6 +26,8 @@ extern "C" {
#define NUM_HITLAG_STATES (9)
#define NUM_HITLAG_SOUNDS (4)
void K_AddHitLagFromCollision(mobj_t *mo, INT32 tics);
/*--------------------------------------------------
void K_AddHitLag(mobj_t *mo, INT32 tics, boolean fromDamage);

View file

@ -3307,19 +3307,13 @@ static void K_drawKartEmeralds(void)
INT32 K_GetTransFlagFromFixed(fixed_t value, boolean midrace)
{
fixed_t base = midrace ? GRADINGFACTORSOFTCAP : FRACUNIT;
fixed_t base = FRACUNIT;
value = std::clamp(value, base - FRACUNIT/2, base + FRACUNIT/2);
// Calculate distance from "base""
fixed_t distance = abs(base - value);
if (midrace)
{
if (value > base)
distance = FixedMul(distance, GRADINGFACTORCAPSTRENGTH);
}
distance = std::clamp(distance, 0, FRACUNIT/2);
// Map the distance to 0-10 range (10 = closest to 1.0, 0 = farthest from 1.0)
@ -5189,7 +5183,7 @@ static void K_drawKartPlayerCheck(void)
continue;
}
if ((checkplayer->invincibilitytimer <= 0) && (leveltime & 2))
if ((checkplayer->invincibilitytimer <= 0) && (leveltime & 2) && !(cv_reducevfx.value))
{
pnum++; // white frames
}
@ -7172,11 +7166,13 @@ static void K_drawLapStartAnim(void)
oldval = (188 + (32 * std::max(0, progressOld - 76))) * FRACUNIT;
interpx = R_InterpolateFixed(oldval, newval);
UINT32 sanitizedlaps = std::min((UINT32)99, (UINT32)stplyr->latestlap);
V_DrawFixedPatch(
interpx, // 194
30*FRACUNIT, // 24
FRACUNIT, V_SNAPTOTOP|hudtransflags,
kp_lapanim_number[(((UINT32)stplyr->latestlap) / 10)][std::min(progress/2-8, 2)], NULL);
kp_lapanim_number[(sanitizedlaps / 10)][std::min(progress/2-8, 2)], NULL);
if (progress/2-10 >= 0)
{
@ -7188,7 +7184,7 @@ static void K_drawLapStartAnim(void)
interpx, // 221
30*FRACUNIT, // 24
FRACUNIT, V_SNAPTOTOP|hudtransflags,
kp_lapanim_number[(((UINT32)stplyr->latestlap) % 10)][std::min(progress/2-10, 2)], NULL);
kp_lapanim_number[(sanitizedlaps % 10)][std::min(progress/2-10, 2)], NULL);
}
}
}
@ -7311,7 +7307,21 @@ static void K_drawDistributionDebugger(void)
return;
}
K_FillItemRouletteData(stplyr, &rouletteData, false, true);
{
// GROSS GROSS GROSS GROSS copypaste from K_FillItemRoulette
// but without the potential for Lua side-effects etc.
// This sucks the ass.
K_InitRoulette(&rouletteData);
rouletteData.baseDist = K_UndoMapScaling(stplyr->distancetofinish);
if (stplyr->pflags & PF_AUTOROULETTE)
rouletteData.autoroulette = true;
K_CalculateRouletteSpeed(&rouletteData);
K_FillItemRouletteData(stplyr, &rouletteData, false, true);
}
if (cv_kartdebugdistribution.value <= 1)
return;
@ -7772,7 +7782,7 @@ void K_drawKartHUD(void)
return;
}
if (staffsync)
if (staffsync && staffsync_total)
{
V_DrawFadeScreen(31, 8);
V_DrawCenteredGamemodeString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2 - 30, 0, 0, "Staff Ghost Sync Test");

View file

@ -158,9 +158,8 @@ fixed_t K_EffectiveGradingFactor(const player_t *player)
return min;
fixed_t gf = player->gradingfactor;
if (gf > GRADINGFACTORSOFTCAP && !K_PlayerUsesBotMovement(player))
gf = GRADINGFACTORSOFTCAP + FixedDiv(gf - GRADINGFACTORSOFTCAP, GRADINGFACTORCAPSTRENGTH);
if (franticitems)
gf = (gf + FRACUNIT)/2;
return max(min, gf);
}
@ -508,7 +507,7 @@ boolean K_IsPlayerLosing(player_t *player)
}
// Some behavior should change if the player approaches the frontrunner unusually fast.
fixed_t K_PlayerScamPercentage(const player_t *player, UINT8 mult)
fixed_t K_PlayerScamPercentage(const player_t *player, fixed_t mult)
{
if (!M_NotFreePlay())
return 0;
@ -523,7 +522,7 @@ fixed_t K_PlayerScamPercentage(const player_t *player, UINT8 mult)
// "Why 2000?" Vibes
UINT32 distance = K_GetItemRouletteDistance(player, 8);
UINT32 scamdistance = mult * SCAMDIST;
UINT32 scamdistance = FixedMul(mult, SCAMDIST*FRACUNIT)/FRACUNIT;
if (distance >= scamdistance)
return 0;
@ -3172,26 +3171,70 @@ boolean K_SlopeResistance(const player_t *player)
fixed_t K_PlayerTripwireSpeedThreshold(const player_t *player)
{
fixed_t required_speed = 2 * K_GetKartSpeed(player, false, false); // 200%
fixed_t base_speed = K_GetKartSpeed(player, false, false);
fixed_t required_speed = 9 * base_speed / 4; // 225%
// 200% in Easy / Tutorial
if (gamespeed == KARTSPEED_EASY)
required_speed = 2 * base_speed;
if (K_LegacyRingboost(player))
return required_speed;
return 2 * base_speed;
// 150% in special
if (specialstageinfo.valid)
required_speed = 3 * K_GetKartSpeed(player, false, false) / 2; // 150%
required_speed = 3 * base_speed / 2;
// 400% in Time Attack
if (modeattacking && !(gametyperules & GTR_CATCHER))
required_speed = 4 * K_GetKartSpeed(player, false, false);
required_speed = 4 * base_speed;
// Race
if ((gametyperules & GTR_CIRCUIT) && !K_Cooperative() && M_NotFreePlay() && !modeattacking)
{
required_speed += FixedMul(required_speed, K_PlayerScamPercentage(player, 2)); // Proration: Players near 1st need more speed!
}
/*
All of this will be for making Sonic Boom easier when you're drowning in the back, like a "reverse" proration
*/
if (player->offroad && K_ApplyOffroad(player))
{
// Increase to 300% if you're lawnmowering.
required_speed = (required_speed * 3) / 2;
#define REVERSED_SONICBOOM_PRORATION (30000)
#define MAX_SONICBOOM_REDUCTION (8*FRACUNIT/10)
UINT32 dist = K_GetItemRouletteDistance(player, D_NumPlayersInRace());
if (dist > REVERSED_SONICBOOM_PRORATION)
{
dist = REVERSED_SONICBOOM_PRORATION;
}
fixed_t distfactor = FixedDiv(dist, REVERSED_SONICBOOM_PRORATION); //
fixed_t sonicboom_aid = Easing_InCubic(distfactor, FRACUNIT, MAX_SONICBOOM_REDUCTION);
required_speed = FixedMul(sonicboom_aid, required_speed);
/*
And then all of this will be for making it harder when you're in scam range, actual proration
*/
fixed_t scamcheck_in_2p = 3*FRACUNIT/2; // Lower values = need to be closer to be scamming
fixed_t scamcheck_in_16p = 5*FRACUNIT/2; // Higher values = tripwire threshold goes up when further away
fixed_t scamscaler = FixedRescale(D_NumPlayersInRace(), 2, 16, Easing_Linear, scamcheck_in_2p, scamcheck_in_16p);
required_speed += FixedMul(required_speed, K_PlayerScamPercentage(player, scamscaler));
if (player->position == 1)
{
required_speed = 9 * K_GetKartSpeed(player, false, false); // Seek employment
}
#if 0
if (!K_PlayerUsesBotMovement(player)) // Sonic Boom debug
{
//CONS_Printf("Sonic Boom threshold: %d percent, IN FRACUNIT: %d \n", ((required_speed *100) / K_GetKartSpeed(player, false, false)), required_speed);
CONS_Printf("D=%d DF=%d SBA=%d SCAM=%d RRS=%d\n", dist, distfactor, sonicboom_aid, K_PlayerScamPercentage(player, scamscaler), required_speed * 100 / base_speed);
}
#endif
#undef REVERSED_SONICBOOM_PRORATION
#undef MAX_SONICBOOM_REDUCTION
}
if (player->botvars.rubberband > FRACUNIT && K_PlayerUsesBotMovement(player) == true)
@ -3670,7 +3713,7 @@ static void K_GetKartBoostPower(player_t *player)
boostpower = (4*boostpower)/5;
if (player->stonedrag)
boostpower = (4*boostpower)/5;
boostpower = (70*boostpower)/100;
// Note: Handling will ONLY stack when sliptiding!
// > (NB 2023-03-06: This was previously unintentionally applied while drifting as well.)
@ -4498,6 +4541,15 @@ void K_SpawnEXP(player_t *player, UINT8 exp, mobj_t *impact)
if (exp == 0)
return;
boolean special = false;
if (player->gradingpointnum == K_GetNumGradingPoints())
{
exp *= 3;
special = true;
}
for (int i = 0; i < exp; i++)
{
mobj_t *pickup = P_SpawnMobj(impact->x, impact->y, impact->z, MT_EXP);
@ -4508,6 +4560,14 @@ void K_SpawnEXP(player_t *player, UINT8 exp, mobj_t *impact)
pickup->momy += P_RandomRange(PR_ITEM_DEBRIS, -20*mapobjectscale, 20*mapobjectscale);
pickup->momz += P_RandomRange(PR_ITEM_DEBRIS, -20*mapobjectscale, 20*mapobjectscale);
// pickup->color = player->skincolor;
if (special)
{
P_InstaScale(pickup, 3*pickup->scale/2);
pickup->color = SKINCOLOR_SAPPHIRE;
pickup->colorized = true;
}
P_SetTarget(&pickup->target, player->mo);
}
}
@ -4717,7 +4777,7 @@ void K_CheckpointCrossAward(player_t *player)
K_HandleRaceSplits(player, leveltime - starttime, player->gradingpointnum);
}
player->gradingfactor += K_GetGradingFactorAdjustment(player);
player->gradingfactor += K_GetGradingFactorAdjustment(player, player->gradingpointnum);
player->gradingpointnum++;
player->exp = K_GetEXP(player);
//CONS_Printf("player: %s factor: %.2f exp: %d\n", player_names[player-players], FIXED_TO_FLOAT(player->gradingfactor), player->exp);
@ -4908,8 +4968,10 @@ void K_DoInstashield(player_t *player)
void K_DoPowerClash(mobj_t *t1, mobj_t *t2) {
mobj_t *clash;
UINT8 lag1 = 5;
UINT8 lag2 = 5;
UINT8 lag1 = 10; // Base value used for kartitem-to-player collision.
UINT8 lag2 = 10; // We want to preserve shooting invinc players to hinder them!
boolean slow1 = false; // If we _are_ hitting a kartitem, keep that value.
boolean slow2 = false; // Otherwise, route to K_AddHitLagFromCollision.
boolean stripbubble = (gametyperules & GTR_BUMPERS);
@ -4917,24 +4979,32 @@ void K_DoPowerClash(mobj_t *t1, mobj_t *t2) {
if (t1->player)
{
t1->player->instashield = 1;
t1->player->speedpunt += 20;
lag1 -= min(lag1, t1->player->speedpunt/10);
if (stripbubble && t1->player->curshield == KSHIELD_BUBBLE)
K_PopBubbleShield(t1->player);
if (P_IsKartFieldItem(t2->type))
slow1 = true;
}
if (t2->player)
{
t2->player->instashield = 1;
t2->player->speedpunt += 20;
lag2 -= min(lag1, t2->player->speedpunt/10);
if (stripbubble && t2->player->curshield == KSHIELD_BUBBLE)
K_PopBubbleShield(t2->player);
if (P_IsKartFieldItem(t1->type))
slow2 = true;
}
S_StartSound(t1, sfx_parry);
K_AddHitLag(t1, lag1+1, false);
K_AddHitLag(t2, lag2+1, false);
if (slow1)
K_AddHitLag(t1, lag1, false);
else
K_AddHitLagFromCollision(t1, lag1);
if (slow2)
K_AddHitLag(t2, lag2, false);
else
K_AddHitLagFromCollision(t2, lag2);
clash = P_SpawnMobj((t1->x/2) + (t2->x/2), (t1->y/2) + (t2->y/2), (t1->z/2) + (t2->z/2), MT_POWERCLASH);
@ -5848,7 +5918,7 @@ void K_ApplyTripWire(player_t *player, tripwirestate_t state)
if (player->hyudorotimer <= 0)
{
K_AddHitLag(player->mo, 10, false);
K_AddHitLag(player->mo, (state == TRIPSTATE_PASSED) ? 2 : 10, false);
player->mo->hitlag -= min(player->mo->hitlag, player->tripwireUnstuck/4);
}
@ -9672,7 +9742,7 @@ static void K_UpdateTripwire(player_t *player)
tripwirepass_t triplevel = K_TripwirePassConditions(player);
boolean mightplaysound = false;
if (triplevel != TRIPWIRE_NONE)
if (triplevel != TRIPWIRE_NONE) // Sonic Boom, able to pass tripwire
{
if (!boostExists)
{
@ -9767,7 +9837,7 @@ boolean K_PressingEBrake(const player_t *player)
void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
{
const boolean onground = P_IsObjectOnGround(player->mo);
const fixed_t scamming = K_PlayerScamPercentage(player, 1);
const fixed_t scamming = K_PlayerScamPercentage(player, FRACUNIT);
/* reset sprite offsets :) */
player->mo->sprxoff = 0;
@ -9878,7 +9948,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
{
mobj_t *ghost;
ghost = P_SpawnGhostMobj(player->mo);
ghost->extravalue1 = player->numboosts+1;
ghost->extravalue1 = player->numboosts;
ghost->extravalue2 = (leveltime % ghost->extravalue1);
ghost->fuse = ghost->extravalue1;
ghost->renderflags |= RF_FULLBRIGHT;
@ -9915,7 +9985,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
// Race: spawn ring debt indicator
// Battle: spawn zero-bumpers indicator
if (!(player->pflags2 & PF2_UNSTINGABLE) && ((gametyperules & GTR_SPHERES) ? player->mo->health <= 1 : RINGTOTAL(player) <= 0))
if (!(player->pflags2 & PF2_UNSTINGABLE) && player->ringboostinprogress == 0 && ((gametyperules & GTR_SPHERES) ? player->mo->health <= 1 : RINGTOTAL(player) <= 0))
{
UINT8 doubler;
@ -10446,7 +10516,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
player->tricklock = 0;
}
if (P_PlayerInPain(player) || player->respawn.state != RESPAWNST_NONE)
if ((P_PlayerInPain(player) && G_CompatLevel(0x0010)) || player->respawn.state != RESPAWNST_NONE)
{
player->ringboost = 0;
}
@ -10474,6 +10544,11 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
// CONS_Printf("%d - %d\n", player->ringboost, oldringboost - player->ringboost);
}
if (!G_CompatLevel(0x0010) && player->superring == 0 && player->ringboxdelay == 0 && player->ringboost < player->lastringboost)
{
player->lastringboost = player->ringboost;
}
if (player->sneakertimer)
{
player->sneakertimer--;
@ -10507,10 +10582,6 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
if (player->trickboost)
player->trickboost--;
/*
if (K_PlayerUsesBotMovement(player) && player->botvars.bumpslow && player->incontrol)
player->botvars.bumpslow--;
*/
// WHOOPS! 2.4 bots were tuned around a bugged version of bumpslow that NEVER decayed
// if the player in slot 0 was a human. People seem to like this tuning, but the dampened
@ -10520,8 +10591,17 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
// I'd like to retune this later, but for now, just set bumpslow on every bot, as if they all
// contact a wall instantly—consistently giving them the softer rubberband advancement.
// What the fuck making games is hard.
if (K_PlayerUsesBotMovement(player))
player->botvars.bumpslow = TICRATE*2;
if (G_CompatLevel(0x0010))
{
// Backwards compatibility for bot takeover in staff ghosts.
if (K_PlayerUsesBotMovement(player) && player->botvars.bumpslow && player->incontrol)
player->botvars.bumpslow--;
}
else
{
if (K_PlayerUsesBotMovement(player))
player->botvars.bumpslow = TICRATE*2;
}
if (player->flamedash)
@ -10639,13 +10719,6 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
player->wavedashpower = FRACUNIT; // Safety
}
if (player->speedpunt)
player->speedpunt--;
// This timer can get out of control fast, clamp to match player expectations about "new" hazards
if (player->speedpunt > TICRATE*4)
player->speedpunt = TICRATE*4;
if (player->trickcharge > 0 && onground == true)
{
player->trickcharge--;
@ -10685,6 +10758,9 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
}
}
if (player->ringboostinprogress)
player->ringboostinprogress--;
if (player->baildrop)
{
// freeze the stunned timer while baildrop is active
@ -10791,6 +10867,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
player->itemamount = 0;
player->itemtype = 0;
player->rocketsneakertimer = 0;
/*
if (player->itemamount)
@ -10814,7 +10891,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
}
INT32 fls = K_GetEffectiveFollowerSkin(player);
if (player->follower && fls >= 0 && fls < numfollowers)
if (player->follower && fls >= 0 && fls < numfollowers && cv_karthorns.value)
{
const follower_t *fl = &followers[fls];
S_StartSound(player->follower, fl->hornsound);
@ -13565,7 +13642,7 @@ static INT32 K_FlameShieldMax(player_t *player)
disttofinish = K_GetItemRouletteDistance(player, 8);
if (D_NumPlayersInRace() <= 1)
if (D_NumPlayersInRace() <= 1 || (gametyperules & GTR_CATCHER) || (gametype == GT_TUTORIAL))
{
return FLAMESHIELD_MAX; // max when alone, for testing
// and when in battle, for chaos
@ -14049,12 +14126,19 @@ static void K_KartSpindash(player_t *player)
}
else if (!G_CompatLevel(0x0010))
{
boolean ebrakelasttic = ((player->oldcmd.buttons & BT_EBRAKEMASK) == BT_EBRAKEMASK);
if (player->pflags2 & PF2_STRICTFASTFALL)
ebrakelasttic = (player->oldcmd.buttons & BT_SPINDASH);
UINT16 oldbuttons = player->oldcmd.buttons;
UINT16 nowbuttons = K_GetKartButtons(player);
if (K_KartKickstart(player))
oldbuttons |= BT_ACCELERATE; // Not strictly correct, but better than nothing.
// Kickstart needs substantial attention if we want this sort of thing to be clean.
boolean ebrakelasttic = ((oldbuttons & BT_EBRAKEMASK) == BT_EBRAKEMASK);
if (player->pflags2 & PF2_STRICTFASTFALL && !(oldbuttons & BT_SPINDASH))
ebrakelasttic = false;
boolean ebrakenow = K_PressingEBrake(player);
if (player->pflags2 & PF2_STRICTFASTFALL && !(player->cmd.buttons & BT_SPINDASH))
if (player->pflags2 & PF2_STRICTFASTFALL && !(nowbuttons & BT_SPINDASH))
ebrakenow = false;
if (!ebrakelasttic && ebrakenow && player->fastfall && player->transfer)
@ -14844,7 +14928,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
if (player->cmd.buttons & BT_BAIL && (player->cmd.buttons & BT_RESPAWNMASK) != BT_RESPAWNMASK)
{
if (leveltime < introtime || (gametyperules & GTR_SPHERES) || modeattacking || player->markedfordeath
|| player->respawn.state != RESPAWNST_NONE)
|| player->respawn.state != RESPAWNST_NONE || player->baildrop)
{
// No bailing in GTR_SPHERES because I cannot be fucked to do manual Last Chance right now.
// Maybe someday!
@ -14971,6 +15055,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
{
P_SetOrigin(ring, ring->x, ring->y, ring->z);
ring->extravalue1 = 1;
player->ringboostinprogress = 25;
}
@ -15498,7 +15583,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
}
player->growshrinktimer = max(0, player->growshrinktimer);
player->growshrinktimer += ((gametyperules & GTR_CLOSERPLAYERS) ? 8 : 12) * TICRATE;
player->growshrinktimer = max(player->growshrinktimer + 5*TICRATE, ((gametyperules & GTR_CLOSERPLAYERS) ? 8 : 12) * TICRATE);
S_StartSound(player->mo, sfx_kc5a);
@ -17028,6 +17113,16 @@ boolean K_PlayerCanPunt(player_t *player)
return true;
}
if (player->overshield > 0)
{
return true;
}
if (player->lightningcharge > 0)
{
return true;
}
if (player->tripwirePass >= TRIPWIRE_BLASTER && player->speed >= K_PlayerTripwireSpeedThreshold(player))
{
return true;
@ -17083,7 +17178,19 @@ static UINT8 K_Opponents(player_t *player)
return opponents;
}
static fixed_t K_GradingFactorPower(player_t *player)
fixed_t K_FinalCheckpointPower(void)
{
// How much of the final total is given out as a bonus for the last check?
fixed_t FINAL_CHECK_PERCENT = 25*FRACUNIT/100;
fixed_t theentirerace = K_GetNumGradingPoints()*FRACUNIT;
fixed_t theentireraceplusbonus = FixedDiv(theentirerace, FRACUNIT - FINAL_CHECK_PERCENT);
fixed_t bonusonly = theentireraceplusbonus - theentirerace;
return bonusonly;
}
static fixed_t K_GradingFactorPower(player_t *player, UINT32 gradingpoint)
{
fixed_t power = EXP_POWER; // adjust to change overall exp volatility
UINT8 opponents = K_Opponents(player);
@ -17097,23 +17204,30 @@ static fixed_t K_GradingFactorPower(player_t *player)
if (opponents > 8)
power -= (opponents - 8) * (power/24);
UINT32 gp = K_GetNumGradingPoints();
if (gradingpoint-1 == gp)
{
power += FixedMul(power, K_FinalCheckpointPower());
}
return power;
}
static fixed_t K_GradingFactorGainPerWin(player_t *player)
static fixed_t K_GradingFactorGainPerWin(player_t *player, UINT32 gradingpoint)
{
return K_GradingFactorPower(player);
return K_GradingFactorPower(player, gradingpoint);
}
static fixed_t K_GradingFactorDrainPerCheckpoint(player_t *player)
static fixed_t K_GradingFactorDrainPerCheckpoint(player_t *player, UINT32 gradingpoint)
{
// EXP_STABLERATE: How low do you have to place before losing gradingfactor? 4*FRACUNIT/10 = top 40% of race gains, 60% loses.
UINT8 opponents = K_Opponents(player);
fixed_t power = K_GradingFactorPower(player);
fixed_t power = K_GradingFactorPower(player, gradingpoint);
return FixedMul(power, FixedMul(opponents*FRACUNIT, FRACUNIT - EXP_STABLERATE));
}
fixed_t K_GetGradingFactorAdjustment(player_t *player)
fixed_t K_GetGradingFactorAdjustment(player_t *player, UINT32 gradingpoint)
{
fixed_t result = 0;
@ -17124,13 +17238,13 @@ fixed_t K_GetGradingFactorAdjustment(player_t *player)
continue;
if (player->position < players[i].position)
result += K_GradingFactorGainPerWin(player);
result += K_GradingFactorGainPerWin(player, gradingpoint);
}
// ...then take all of the gradingfactor you could possibly have earned,
// and lose it proportional to the stable rate. If you're below
// the stable threshold, this results in you losing gradingfactor
result -= K_GradingFactorDrainPerCheckpoint(player);
result -= K_GradingFactorDrainPerCheckpoint(player, gradingpoint);
return result;
}
@ -17144,8 +17258,8 @@ fixed_t K_GetGradingFactorMinMax(player_t *player, boolean max)
for (UINT8 i = 0; i < player->gradingpointnum; i++) // For each gradingpoint you've reached...
{
for (UINT8 j = 0; j < winning; j++)
factor += K_GradingFactorGainPerWin(player); // If max, increase gradingfactor for each player you could have been beating.
factor -= K_GradingFactorDrainPerCheckpoint(player); // Then, drain like usual.
factor += K_GradingFactorGainPerWin(player, i); // If max, increase gradingfactor for each player you could have been beating.
factor -= K_GradingFactorDrainPerCheckpoint(player, i); // Then, drain like usual.
}
return factor;
@ -17153,19 +17267,33 @@ fixed_t K_GetGradingFactorMinMax(player_t *player, boolean max)
UINT16 K_GetEXP(player_t *player)
{
fixed_t gradingpointnum = FRACUNIT * player->gradingpointnum;
UINT32 numgradingpoints = K_GetNumGradingPoints();
fixed_t targetminexp = (EXP_MIN*player->gradingpointnum<<FRACBITS) / max(1,numgradingpoints); // about what a last place player should be at this stage of the race
fixed_t targetmaxexp = (EXP_MAX*player->gradingpointnum<<FRACBITS) / max(1,numgradingpoints); // about what a 1.0 factor should be at this stage of the race
fixed_t fixedgradingpoints = numgradingpoints * FRACUNIT;
fixed_t effgradingpoints = fixedgradingpoints + K_FinalCheckpointPower();
// Account for Final Check bonus
if (player->gradingpointnum == numgradingpoints)
gradingpointnum = effgradingpoints;
// fixed_t targetminexp = (EXP_MIN*gpn<<FRACBITS) / max(1,effgradingpoints); // about what a last place player should be at this stage of the race
// fixed_t targetmaxexp = (EXP_MAX*gpn<<FRACBITS) / max(1,effgradingpoints); // about what a 1.0 factor should be at this stage of the race
fixed_t targetminexp = FixedDiv(EXP_MIN * gradingpointnum, max(FRACUNIT, effgradingpoints));
fixed_t targetmaxexp = FixedDiv(EXP_MAX * gradingpointnum, max(FRACUNIT, effgradingpoints));
fixed_t factormin = K_GetGradingFactorMinMax(player, false);
fixed_t factormax = K_GetGradingFactorMinMax(player, true);
UINT16 exp = FixedRescale(player->gradingfactor, factormin, factormax, Easing_Linear, targetminexp, targetmaxexp)>>FRACBITS;
if (modeattacking)
exp = 100 * player->gradingpointnum / numgradingpoints;
exp = EXP_MAX * player->gradingpointnum / max(1, numgradingpoints); // No Final Check here, just a linear slide
// CONS_Printf("Player %s numgradingpoints=%d gradingpoint=%d targetminexp=%d targetmaxexp=%d factor=%.2f factormin=%.2f factormax=%.2f exp=%d\n",
// player_names[player - players], numgradingpoints, player->gradingpointnum, targetminexp, targetmaxexp, FIXED_TO_FLOAT(player->gradingfactor), FIXED_TO_FLOAT(factormin), FIXED_TO_FLOAT(factormax), exp);
/*
if (!player->bot)
CONS_Printf("Player %s fcp=%d effgradingpoints=%d gradingpoint=%d targetminexp=%d targetmaxexp=%d factor=%.2f factormin=%.2f factormax=%.2f exp=%d\n",
player_names[player - players], K_FinalCheckpointPower(), effgradingpoints, gradingpointnum, targetminexp, targetmaxexp, FIXED_TO_FLOAT(player->gradingfactor), FIXED_TO_FLOAT(factormin), FIXED_TO_FLOAT(factormax), exp);
*/
return exp;
}
@ -17414,7 +17542,7 @@ fixed_t K_TeamComebackMultiplier(player_t *player)
void K_ApplyStun(player_t *player, mobj_t *inflictor, mobj_t *source, ATTRUNUSED INT32 damage, ATTRUNUSED UINT8 damagetype)
{
#define BASE_STUN_TICS_MIN (4 * TICRATE)
#define BASE_STUN_TICS_MAX (10 * TICRATE)
#define BASE_STUN_TICS_MAX (8 * TICRATE)
#define MAX_STUN_REDUCTION (FRACUNIT/2)
#define STUN_REDUCTION_DISTANCE (20000)
INT32 stunTics = 0;

View file

@ -42,7 +42,7 @@ Make sure this matches the actual number of states
#define INSTAWHIP_HOLD_DELAY (TICRATE*2)
// MUST be longer or equal to INSTAWHIP_CHARGETIME.
#define INSTAWHIP_TETHERBLOCK (TICRATE*4)
#define PUNISHWINDOW (10*TICRATE/10)
#define PUNISHWINDOW (G_CompatLevel(0x0010) ? 7*TICRATE/10 : 10*TICRATE/10)
#define BAIL_MAXCHARGE (84) // tics to bail when in painstate nad in air, on ground is half, if you touch this, also update Obj_BailChargeThink synced animation logic
#define BAIL_DROP (FRACUNIT) // How many rings it has to drop before stun starts
@ -76,7 +76,7 @@ Make sure this matches the actual number of states
#define EARLY_ITEM_FLICKER (NUMTRANSMAPS)
#define TRIPWIRE_OK_SOUND (sfx_s3k40)
#define TRIPWIRE_OK_SOUND (sfx_sonbo2)
#define TRIPWIRE_NG_SOUND (sfx_gshaf)
// 2023-08-26 +ang20 to Sal's OG values to make them friendlier - Tyron
@ -105,7 +105,7 @@ Make sure this matches the actual number of states
#define MAXTOPACCEL (12*FRACUNIT)
#define TOPACCELREGEN (FRACUNIT/16)
#define BUBBLESCAM (4)
#define BUBBLESCAM (4*FRACUNIT)
// Handling boosts and sliptide conditions got weird.
// You must be under a handling boost of at least SLIPTIDEHANDLING to sliptide.
@ -132,11 +132,10 @@ boolean K_DuelItemAlwaysSpawns(mapthing_t *mt);
boolean K_InRaceDuel(void);
player_t *K_DuelOpponent(player_t *player);
fixed_t K_FinalCheckpointPower(void);
fixed_t K_EffectiveGradingFactor(const player_t *player);
#define MINGRADINGFACTOR (FRACUNIT/2)
#define MINFRANTICFACTOR (8*FRACUNIT/10)
#define GRADINGFACTORSOFTCAP (FRACUNIT)
#define GRADINGFACTORCAPSTRENGTH (3*FRACUNIT)
void K_TimerReset(void);
void K_TimerInit(void);
@ -145,7 +144,7 @@ UINT32 K_GetPlayerDontDrawFlag(player_t *player);
void K_ReduceVFXForEveryone(mobj_t *mo);
boolean K_IsPlayerLosing(player_t *player);
fixed_t K_PlayerScamPercentage(const player_t *player, UINT8 mult);
fixed_t K_PlayerScamPercentage(const player_t *player, fixed_t mult);
fixed_t K_GetKartGameSpeedScalar(SINT8 value);
INT32 K_GetShieldFromItem(INT32 item);
@ -347,7 +346,7 @@ boolean K_ThunderDome(void);
boolean K_PlayerCanUseItem(player_t *player);
fixed_t K_GetGradingFactorAdjustment(player_t *player);
fixed_t K_GetGradingFactorAdjustment(player_t *player, UINT32 gradingpoint);
fixed_t K_GetGradingFactorMinMax(player_t *player, boolean max);
UINT16 K_GetEXP(player_t *player);

View file

@ -661,7 +661,7 @@ typedef enum
MA_NO
} manswer_e;
#define MAXMENUMESSAGE 256
#define MAXMENUMESSAGE 448
#define MENUMESSAGECLOSE 2
extern struct menumessage_s
{
@ -769,6 +769,7 @@ void M_StartControlPanel(void);
void M_ValidateRestoreMenu(void);
menu_t *M_SpecificMenuRestore(menu_t *torestore);
void M_ClearMenus(boolean callexitmenufunc);
void M_ClearMenusNoTitle(boolean callexitmenufunc);
void M_SelectableClearMenus(INT32 choice);
void M_SetupNextMenu(menu_t *menudef, boolean nofade);
void M_GoBack(INT32 choice);

View file

@ -912,10 +912,6 @@ void M_ClearMenus(boolean callexitmenufunc)
if (currentMenu->quitroutine && callexitmenufunc && !currentMenu->quitroutine())
return; // we can't quit this menu (also used to set parameter from the menu)
#ifndef DC // Save the config file. I'm sick of crashing the game later and losing all my changes!
COM_BufAddText(va("saveconfig \"%s\" -silent\n", configfile));
#endif //Alam: But not on the Dreamcast's VMUs
currentMenu->lastOn = itemOn;
if (gamestate == GS_MENU) // Back to title screen
@ -934,6 +930,24 @@ void M_ClearMenus(boolean callexitmenufunc)
menuactive = false;
}
void M_ClearMenusNoTitle(boolean callexitmenufunc)
{
if (!menuactive)
return;
CON_ClearHUD();
if (currentMenu->quitroutine && callexitmenufunc && !currentMenu->quitroutine())
return; // we can't quit this menu (also used to set parameter from the menu)
currentMenu->lastOn = itemOn;
M_AbortVirtualKeyboard();
menumessage.active = false;
menuactive = false;
}
void M_SelectableClearMenus(INT32 choice)
{
(void)choice;

View file

@ -384,14 +384,20 @@ void K_UpdatePowerLevels(player_t *player, UINT8 gradingpoint, boolean forfeit)
}
else
{
if (exitBonus == false)
fixed_t prevInc = ourinc;
INT16 dvs = max(K_GetNumGradingPoints(), 1);
ourinc = FixedDiv(ourinc, dvs*FRACUNIT);
theirinc = FixedDiv(theirinc, dvs*FRACUNIT);
if (exitBonus)
{
ourinc = FixedMul(ourinc, FRACUNIT + K_FinalCheckpointPower());
theirinc = FixedMul(theirinc, FRACUNIT + K_FinalCheckpointPower());
CONS_Debug(DBG_PWRLV, "Final check bonus (%d / %d * %d = %d)\n", prevInc/FRACUNIT, dvs, K_FinalCheckpointPower(), ourinc/FRACUNIT);
}
else
{
fixed_t prevInc = ourinc;
INT16 dvs = max(K_GetNumGradingPoints(), 1);
ourinc = FixedDiv(ourinc, dvs*FRACUNIT);
theirinc = FixedDiv(theirinc, dvs*FRACUNIT);
CONS_Debug(DBG_PWRLV, "Reduced (%d / %d = %d) because it's not the end of the race\n", prevInc/FRACUNIT, dvs, ourinc/FRACUNIT);
}
}
@ -432,6 +438,8 @@ void K_UpdatePowerLevelsFinalize(player_t *player, boolean onForfeit)
if (checksleft <= 0)
{
if (!(gametyperules & GTR_CHECKPOINTS)) // We should probably do at least _one_ PWR update.
K_UpdatePowerLevels(player, player->gradingpointnum, onForfeit);
// We've done every checkpoint already.
return;
}

View file

@ -423,8 +423,8 @@ static void K_DrawFinishLineBeamForLine(fixed_t offset, angle_t aiming, line_t *
--------------------------------------------------*/
void K_RunFinishLineBeam(void)
{
if ((gametyperules & GTR_ROLLINGSTART) || !(leveltime < starttime || rainbowstartavailable == true))
{
if ((gametyperules & GTR_ROLLINGSTART) || !(leveltime < starttime || rainbowstartavailable == true) || P_LevelIsFrozen())
{
return;
}

View file

@ -176,6 +176,12 @@ void K_DoIngameRespawn(player_t *player)
K_DoFault(player);
}
if (player->rings <= -20 && !player->respawn.fromRingShooter)
{
P_KillMobj(player->mo, NULL, NULL, DMG_INSTAKILL);
return;
}
player->ringboost = 0;
player->driftboost = player->strongdriftboost = 0;
player->gateBoost = 0;
@ -740,7 +746,7 @@ static void K_DropDashWait(player_t *player)
player->respawn.timer--;
if (player->pflags & PF_FAULT)
return;
return;
if (leveltime % 8 == 0)
{

View file

@ -80,7 +80,7 @@
static UINT32 K_DynamicItemOddsRace[NUMKARTRESULTS-1][2] =
{
// distance, duplication tolerance
{20, 10}, // sneaker
{25, 10}, // sneaker
{63, 12}, // rocketsneaker
{60, 19}, // invincibility
{8, 4}, // banana
@ -491,12 +491,7 @@ UINT32 K_ScaleItemDistance(INT32 distance, UINT8 numPlayers)
return distance;
}
/*--------------------------------------------------
static UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers)
See header file for description.
--------------------------------------------------*/
UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers)
static UINT32 K_GetUnscaledFirstDistance(const player_t *player)
{
UINT32 pdis = 0;
@ -546,6 +541,19 @@ UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers)
}
pdis = K_UndoMapScaling(pdis);
return pdis;
}
/*--------------------------------------------------
static UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers)
See header file for description.
--------------------------------------------------*/
UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers)
{
UINT32 pdis = K_GetUnscaledFirstDistance(player);
pdis = K_ScaleItemDistance(pdis, numPlayers);
if (player->bot && (player->botvars.rival || cv_levelskull.value))
@ -735,7 +743,7 @@ boolean K_ForcedSPB(const player_t *player, itemroulette_t *const roulette)
Return:-
N/A
--------------------------------------------------*/
static void K_InitRoulette(itemroulette_t *const roulette)
void K_InitRoulette(itemroulette_t *const roulette)
{
size_t i;
@ -1047,7 +1055,7 @@ ATTRUNUSED static boolean K_IsItemUselessAlone(kartitems_t item)
}
}
ATTRUNUSED static boolean K_IsItemSpeed(kartitems_t item)
static boolean K_IsItemSpeed(kartitems_t item)
{
switch (item)
{
@ -1059,6 +1067,7 @@ ATTRUNUSED static boolean K_IsItemSpeed(kartitems_t item)
case KRITEM_TRIPLESNEAKER:
case KITEM_FLAMESHIELD:
case KITEM_SHRINK:
case KITEM_SUPERRING:
return true;
default:
return false;
@ -1411,11 +1420,6 @@ void K_FillItemRouletteData(player_t *player, itemroulette_t *const roulette, bo
roulette->preexpdist = K_GetItemRouletteDistance(player, roulette->playing);
roulette->dist = roulette->preexpdist;
if ((gametyperules & GTR_CIRCUIT) && !K_Cooperative())
{
roulette->dist = FixedMul(roulette->preexpdist, K_EffectiveGradingFactor(player));
}
// ===============================================================================
// Dynamic Roulette. Oh boy!
// Alright, here's the broad plan:
@ -1426,15 +1430,41 @@ void K_FillItemRouletteData(player_t *player, itemroulette_t *const roulette, bo
// 5: Skim any items that are much weaker than the reel's average out of the roulette
// 6: Cram it all in
fixed_t largegamescaler = roulette->playing * 14 + 100; // Spread out item odds in large games for a less insane experience.
UINT32 targetpower = 100 * roulette->dist / largegamescaler; // fill roulette with items around this value!
UINT32 powers[NUMKARTRESULTS]; // how strong is each item? think of this as a "target distance" for this item to spawn at
UINT32 deltas[NUMKARTRESULTS]; // how different is that strength from target?
UINT32 candidates[NUMKARTRESULTS]; // how many of this item should we try to insert?
UINT32 dupetolerance[NUMKARTRESULTS]; // how willing are we to select this item after already selecting it? higher values = lower dupe penalty
boolean permit[NUMKARTRESULTS]; // is this item allowed?
UINT32 lonelinessSuppressor = DISTVAR; // This close to 1st? Dampen loneliness (you have a target!)
UINT32 maxEXPDistanceCut = 3*DISTVAR; // The maximum amount you can be displaced by EXP
// If we're too close to 1st in absolute units, crush our top-end item odds down.
fixed_t crowdingFirst = 0;
if (player->position != 1)
crowdingFirst = FixedRescale(K_GetUnscaledFirstDistance(player), 0, 4*DISTVAR, Easing_InCubic, FRACUNIT, 0);
if ((gametyperules & GTR_CIRCUIT) && !K_Cooperative())
{
roulette->dist = FixedMul(roulette->preexpdist, K_EffectiveGradingFactor(player));
if (roulette->dist < roulette->preexpdist)
{
if (roulette->preexpdist - roulette->dist > maxEXPDistanceCut)
{
roulette->dist = roulette->preexpdist - maxEXPDistanceCut;
}
}
}
fixed_t largegamescaler = roulette->playing * 11 + 115; // Spread out item odds in large games for a less insane experience.
if (franticitems)
largegamescaler = 100; // Except in Frantic, where you know what you're getting
UINT32 targetpower = 100 * roulette->dist / largegamescaler; // fill roulette with items around this value!
if (!(specialstageinfo.valid))
targetpower = Easing_Linear(crowdingFirst, targetpower, targetpower/2);
boolean rival = (player->bot && (player->botvars.rival || cv_levelskull.value));
boolean filterweakitems = true; // strip unusually weak items from reel?
UINT8 reelsize = 15; // How many items to attempt to add in prepass?
@ -1488,6 +1518,10 @@ void K_FillItemRouletteData(player_t *player, itemroulette_t *const roulette, bo
{
powers[i] = humanscaler * K_DynamicItemOddsRace[i-1][0];
dupetolerance[i] = K_DynamicItemOddsRace[i-1][1];
// Bias towards attack items when close to the leader, gotta work for the slingshot pass!
if (K_IsItemSpeed(i) && i != KITEM_SUPERRING)
powers[i] = Easing_Linear(crowdingFirst, powers[i], 2*powers[i]);
}
maxpower = max(maxpower, powers[i]);
@ -1586,6 +1620,12 @@ void K_FillItemRouletteData(player_t *player, itemroulette_t *const roulette, bo
loneliness = Easing_InCubic(loneliness, 0, FRACUNIT);
// You are not lonely if you're super close to 1st, even if 3nd is far away.
if (roulette->preexpdist < lonelinessSuppressor)
{
loneliness = FixedRescale(roulette->preexpdist, 0, lonelinessSuppressor, Easing_InCubic, 0, loneliness);
}
// Give interaction items a nudge against initial selection if you're lonely..
for (i = 1; i < NUMKARTRESULTS; i++)
{
@ -1621,6 +1661,13 @@ void K_FillItemRouletteData(player_t *player, itemroulette_t *const roulette, bo
UINT8 added = 0; // How many items added so far?
UINT32 totalreelpower = 0; // How much total item power in the reel? Used for an average later.
UINT32 basepenalty = 4*DISTVAR; // How much to penalize repicked items, to ensure item variety.
// BUT, keep the item distribution tighter if we're close to the frontrunner...
UINT32 penalty = Easing_Linear(crowdingFirst, basepenalty, basepenalty/2);
if (player->position == 1) // ...unless we ARE the frontrunner.
penalty = basepenalty;
for (i = 0; i < reelsize; i++)
{
UINT32 lowestdelta = INT32_MAX;
@ -1647,7 +1694,7 @@ void K_FillItemRouletteData(player_t *player, itemroulette_t *const roulette, bo
// Impose a penalty to this item's delta, to bias against selecting it again.
// This is naively slashed by an item's "duplicate tolerance":
// lower tolerance means that an item is less likely to be reselected (it's "rarer").
UINT32 deltapenalty = 4*DISTVAR*(1+candidates[bestitem])/dupetolerance[bestitem];
UINT32 deltapenalty = penalty*(1+candidates[bestitem])/dupetolerance[bestitem];
// Power items get better odds in frantic, or if you're the rival.
// (For the rival, this is way more likely to matter at lower skills, where they're
@ -1731,10 +1778,10 @@ void K_FillItemRouletteData(player_t *player, itemroulette_t *const roulette, bo
UINT16 BASE_X = 280;
UINT16 BASE_Y = 5+12*debugcount;
INT32 FLAGS = V_SNAPTOTOP|V_SNAPTORIGHT;
V_DrawRightAlignedThinString(BASE_X - 12, 5, FLAGS, va("%d", targetpower/humanscaler));
V_DrawRightAlignedThinString(BASE_X - 12, 5+12, FLAGS, va("%d", toFront));
V_DrawRightAlignedThinString(BASE_X - 12, 5+24, FLAGS, va("%d", toBack));
V_DrawRightAlignedThinString(BASE_X - 12, 5+36, FLAGS, va("%d", loneliness));
V_DrawRightAlignedThinString(BASE_X - 12, 5, FLAGS, va("TP %d", targetpower/humanscaler));
V_DrawRightAlignedThinString(BASE_X - 12, 5+12, FLAGS, va("FB %d / %d", toFront, toBack));
V_DrawRightAlignedThinString(BASE_X - 12, 5+24, FLAGS, va("L %d / CF %d", loneliness, crowdingFirst));
V_DrawRightAlignedThinString(BASE_X - 12, 5+36, FLAGS, va("D %d / %d", roulette->preexpdist, roulette->dist));
for(UINT8 k = 0; k < candidates[i]; k++)
V_DrawFixedPatch((BASE_X + 3*k)*FRACUNIT, (BASE_Y-7)*FRACUNIT, (FRACUNIT >> 1), FLAGS, K_GetSmallStaticCachedItemPatch(i), NULL);
UINT8 amount = K_ItemResultToAmount(i, roulette);

View file

@ -138,6 +138,19 @@ UINT32 K_UndoMapScaling(UINT32 distance);
N/A
--------------------------------------------------*/
void K_InitRoulette(itemroulette_t *const roulette);
/*--------------------------------------------------
static void K_InitRoulette(itemroulette_t *const roulette)
Initializes the data for a new item roulette.
Input Arguments:-
roulette - The item roulette data to initialize.
Return:-
N/A
--------------------------------------------------*/
void K_PushToRouletteItemList(itemroulette_t *const roulette, INT32 item);
/*--------------------------------------------------

View file

@ -225,7 +225,7 @@ INT32 level_tally_t::CalculateGrade(void)
}
}
const INT32 positionWeight = (position > 0 && numPlayers > 2) ? 50 : 0;
const INT32 positionWeight = 0; // (position > 0 && numPlayers > 2) ? 50 : 0;
const INT32 total = positionWeight + bonusWeights[0] + bonusWeights[1];
INT32 ours = 0;

View file

@ -501,7 +501,21 @@ void K_ProcessTerrainEffect(mobj_t *mo)
if (terrain->damageType > 0)
{
UINT8 dmg = (terrain->damageType & 0xFF);
P_DamageMobj(mo, NULL, NULL, 1, dmg);
if ((dmg == DMG_STUMBLE) && !G_CompatLevel(0x0010))
{
if (player->mo->hitlag == 0 &&
(player->mo->momz == 0 || (player->mo->momz > 0) != (P_MobjFlip(player->mo) > 0)))
{
player->pflags2 |= PF2_ALWAYSDAMAGED;
P_DamageMobj(mo, NULL, NULL, 1, dmg);
player->pflags2 &= ~PF2_ALWAYSDAMAGED;
}
}
else
{
P_DamageMobj(mo, NULL, NULL, 1, dmg);
}
}
// Sneaker panel
@ -2011,7 +2025,7 @@ static boolean K_TERRAINLumpParser(char *data, size_t size)
Z_Free(tkn);
tkn = M_GetToken(NULL);
pos = M_GetTokenPos();
if (tkn && pos <= size)
{
if (stricmp(tkn, "optional") == 0)

View file

@ -269,6 +269,8 @@ static int player_get(lua_State *L)
lua_pushboolean(L, plr->transfer);
else if (fastcmp(field,"markedfordeath"))
lua_pushboolean(L, plr->markedfordeath);
else if (fastcmp(field,"mfdfinish"))
lua_pushboolean(L, plr->mfdfinish);
else if (fastcmp(field,"incontrol"))
lua_pushboolean(L, plr->incontrol);
else if (fastcmp(field,"progressivethrust"))
@ -347,6 +349,8 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->spindashspeed);
else if (fastcmp(field,"spindashboost"))
lua_pushinteger(L, plr->spindashboost);
else if (fastcmp(field,"ringboostinprogress"))
lua_pushinteger(L, plr->ringboostinprogress);
else if (fastcmp(field,"fastfall"))
lua_pushfixed(L, plr->fastfall);
else if (fastcmp(field,"fastfallbase"))
@ -375,6 +379,8 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->tripwirePass);
else if (fastcmp(field,"fakeboost"))
lua_pushinteger(L, plr->fakeBoost);
else if (fastcmp(field,"subsonicleniency"))
lua_pushinteger(L, plr->subsonicleniency);
else if (fastcmp(field,"tripwireleniency"))
lua_pushinteger(L, plr->tripwireLeniency);
else if (fastcmp(field,"tripwireairleniency"))
@ -405,8 +411,6 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->overdriveready);
else if (fastcmp(field,"overdrivelenient"))
lua_pushinteger(L, plr->overdrivelenient);
else if (fastcmp(field,"speedpunt"))
lua_pushinteger(L, plr->speedpunt);
else if (fastcmp(field,"trickcharge"))
lua_pushinteger(L, plr->trickcharge);
else if (fastcmp(field,"infinitether"))
@ -948,6 +952,8 @@ static int player_set(lua_State *L)
plr->transfer = luaL_checkboolean(L, 3);
else if (fastcmp(field,"markedfordeath"))
plr->markedfordeath = luaL_checkboolean(L, 3);
else if (fastcmp(field,"mfdfinish"))
plr->mfdfinish = luaL_checkboolean(L, 3);
else if (fastcmp(field,"dotrickfx"))
plr->dotrickfx = luaL_checkboolean(L, 3);
else if (fastcmp(field,"stingfx"))
@ -1012,6 +1018,8 @@ static int player_set(lua_State *L)
plr->spindashspeed = luaL_checkinteger(L, 3);
else if (fastcmp(field,"spindashboost"))
plr->spindashboost = luaL_checkinteger(L, 3);
else if (fastcmp(field,"ringboostinprogress"))
plr->ringboostinprogress = luaL_checkinteger(L, 3);
else if (fastcmp(field,"fastfall"))
plr->fastfall = luaL_checkfixed(L, 3);
else if (fastcmp(field,"fastfallbase"))
@ -1040,6 +1048,8 @@ static int player_set(lua_State *L)
plr->tripwirePass = luaL_checkinteger(L, 3);
else if (fastcmp(field,"fakeboost"))
plr->fakeBoost = luaL_checkinteger(L, 3);
else if (fastcmp(field,"subsonicleniency"))
plr->subsonicleniency = luaL_checkinteger(L, 3);
else if (fastcmp(field,"tripwireleniency"))
plr->tripwireLeniency = luaL_checkinteger(L, 3);
else if (fastcmp(field,"tripwireairleniency"))
@ -1070,8 +1080,6 @@ static int player_set(lua_State *L)
plr->overdriveready = luaL_checkinteger(L, 3);
else if (fastcmp(field,"overdrivelenient"))
plr->overdrivelenient = luaL_checkinteger(L, 3);
else if (fastcmp(field,"speedpunt"))
plr->speedpunt = luaL_checkinteger(L, 3);
else if (fastcmp(field,"trickcharge"))
plr->trickcharge = luaL_checkinteger(L, 3);
else if (fastcmp(field,"infinitether"))

View file

@ -1797,7 +1797,7 @@ boolean M_CheckCondition(condition_t *cn, player_t *player)
case UCRP_PREFIX_BONUSROUND:
return ((grandprixinfo.gp == true) && (grandprixinfo.eventmode == GPEVENT_BONUS));
case UCRP_PREFIX_TIMEATTACK:
return (modeattacking != ATTACKING_NONE);
return (modeattacking != ATTACKING_NONE && !(skins[player->skin]->flags & SF_HIVOLT));
case UCRP_PREFIX_PRISONBREAK:
return ((gametyperules & GTR_PRISONS) && battleprisons);
case UCRP_PREFIX_SEALEDSTAR:
@ -1843,8 +1843,7 @@ boolean M_CheckCondition(condition_t *cn, player_t *player)
if (cn->extrainfo2 != 0)
return (K_PodiumGrade() >= cn->extrainfo1);
if (cn->extrainfo1 != 0)
return (player->position != 0
&& player->position <= cn->extrainfo1);
return K_GetPodiumPosition(player) <= cn->extrainfo1;
return true;
case UCRP_PODIUMEMERALD:
case UCRP_PODIUMPRIZE:

View file

@ -32,6 +32,7 @@
#include "g_game.h"
#include "k_menu.h"
#include "m_cheat.h"
#include "m_random.h"
#include "m_cond.h"
#include "m_pw.h"
#include "m_pw_hash.h"
@ -364,6 +365,111 @@ void f_maps()
}
}
void f_tutorials()
{
UINT16 i;
boolean success = false;
for (i = 0; i < MAXUNLOCKABLES; i++)
{
if (!unlockables[i].conditionset)
continue;
if (unlockables[i].conditionset == CH_FURYBIKE)
continue;
if (gamedata->unlocked[i])
continue;
if (unlockables[i].type != SECRET_MAP)
continue;
UINT16 mapnum = M_UnlockableMapNum(&unlockables[i]);
if (mapnum >= nummapheaders || !mapheaderinfo[mapnum])
continue;
if (G_GuessGametypeByTOL(mapheaderinfo[mapnum]->typeoflevel) != GT_TUTORIAL)
continue;
gamedata->unlocked[i] = true;
success = true;
}
if (success)
{
S_StartSound(0, sfx_kc42);
G_SaveGameData();
}
const char *knucklesrap;
const UINT8 numsections = 5;
static UINT8 section = numsections;
if (section == numsections)
section = M_RandomKey(numsections);
switch (section)
{
default:
knucklesrap =
"\x85""So here's what I'm thinkin', \n"
"\x85"" last time smart guys got together,\n"
"\x85""In a tough sandy place \n"
"\x85"" with a lot of hot weather,\n"
"\x85""Playin' fundamental forces \n"
"\x85"" at the top of their class,\n"
"\x85""They made the sky so much hotter \n"
"\x85"" and that sand into glass!\x80";
break;
case 1:
knucklesrap =
"\x85""But somethin's different, see? \n"
"\x85""My homie right there, I trust him trustin'\n"
"\x85"" the Eggman like it's nothin',\n"
"\x85""A smart little guy \n"
"\x85"" with a brother I like fightin',\n"
"\x85""If there's somethin' cooking \n"
"\x85"" I'm sure he'll do the right thing.\x80";
break;
case 2:
knucklesrap =
"\x85""I watched a space station fall, \n"
"\x85"" straight down, fast fall, into the ground,\n"
"\x85""Pieces of fire, shooting stars, \n"
"\x85"" don't make a wish, kids\n"
"\x85""Last second it's, gone, I ask how, \n"
"\x85"" behold, they call it chaos control,\n"
"\x85""I don't know it, never heard it, seen it, felt it, \n"
"\x85"" sensed it. Even to me, these powers are a mystery.\x80";
break;
case 3:
knucklesrap =
"\x85""The tide goes out, it carries time away, \n"
"\x85"" we call it yesterday,\n"
"\x85""The tide comes in, it rings, it sings, \n"
"\x85"" it brings a new age,\n"
"\x85""But right now it's just me and the water, \n"
"\x85"" thoughts clear, future lookin' hotter,\n"
"\x85""I let myself sink in, feel the waves, \n"
"\x85"" feel my body get lighter.\x80";
break;
case 4:
knucklesrap =
"\x85""Somethin' brewin' at the edges, \n"
"\x85"" that's what a ring is, nothin' but edges,\n"
"\x85""Circled light in a band, \n"
"\x85"" hold 'em in in your hand,\n"
"\x85""But it's a miracle a thousand times over,\n"
"\x85""Small gasps of potential\n"
"\x85"" floatin' over the sand.\x80";
break;
}
section = (section + 1) % numsections;
M_StartMessage("\"Broken Arrow\" ...for Sunbeam Paradise",
va("\"%s\"\n\n%s",
knucklesrap,
(success ? "Unlocked all Tutorials." : "There are no more Tutorials to unlock.")),
NULL, MM_NOTHING, NULL, NULL);
}
void f_characters()
{
UINT16 i;
@ -779,6 +885,7 @@ void M_PasswordInit(void)
passwords.emplace_back(f_colors, "aSk8dw6FzJtTEmovh8fVEtUBpu6lj3QlRT/B5lwiEhAw8dAhRBQLdvtYlPaQcZISWI4wneAfAo6w5d6uf5r++g==");
passwords.emplace_back(f_followers, "zYCIZw2qcnUbtF0P2ybLNHajdl8zrje0hzGex7yuMFe7fj4mvx4AegoMmvir28YvAbfAqkz/ekQRzr+RhrycHw==");
passwords.emplace_back(f_maps, "u/Svaf+DCnCpJ8xmP3AVP4CK6X6X4O3fY73cmIq88ZJEygwz+n+L66q4Vhlv13vWgld1PEyRszFErzflQt9WZw==");
passwords.emplace_back(f_tutorials, "G2FMttJpJ+lI/DeQu8tthL5i7AB4dk8uuksZR1c2N08Zrmpj3vTqWpbhxuSzSrhH10wJfWahR7QOgQdBkDbTdQ==");
passwords.emplace_back(f_characters, "MohmPqpaGSd3MEHLfQKUFl/Yg8pHE+12X1LHEP59Gs/5w1u8mPtGUXNv1GYTF+c8gQqT5hXpZ3FeZ/EfCxo34g==");
passwords.emplace_back(f_altmusic, "dZgxKNagOtB9F7wXqUUPzsuq4tfQlfK8ZqEeFXdI3Hd+k5tYfRm3ToLgbqawaNmwuLVrJ8PB+QnH4gT3ojnTMw==");
passwords.emplace_back(f_timeattack, "mFu5OB9d6jnc2kth7HE66wJ42F/GHDzSvuciK1Qw++6iGnpBccxcKjpoxgOvD3eIoqR606ruBINuXi23proXHQ==");

View file

@ -21,7 +21,42 @@
#include "m_random.h"
#include "m_fixed.h"
char rng_class_names[34][30] = {
"UNDEFINED",
"EXECUTOR",
"ACS",
"DECORATION",
"TERRAIN",
"BUBBLE",
"RANDOMANIM",
"PLAYERSTARTS",
"VOICES",
"RANDOMSKIN",
"RANDOMAUDIENCE",
"RULESCRAMBLE",
"MUSICSELECT",
"ITEM_ROULETTE",
"ITEM_RINGS",
"ITEM_SHRINK",
"ITEM_BUBBLE",
"ITEM_DEBRIS",
"ITEM_BOOST",
"EXPLOSION",
"SMOLDERING",
"SPARKLE",
"MOVINGTARGET",
"TRACKHAZARDD",
"BATTLEUFO",
"BOTS",
"AUTOROULETTE",
"FUZZ",
"FROSTTHROWERS",
"ITEM_SPAWNER",
"TEAMS",
"DUMMY",
"INTERPHUDRANDOM",
"NUISANCE"
};
// ---------------------------
// RNG functions (not synched)

View file

@ -103,6 +103,7 @@ typedef enum
PRNUMCLASS
} pr_class_t;
extern char rng_class_names[34][30];
// M_Random functions pull random numbers of various types that aren't network synced.
// P_Random functions pulls random bytes from a PRNG that is network synced.

View file

@ -438,7 +438,7 @@ void EggTV::watch() const
{
restoreMenu = currentMenu;
M_ClearMenus(false);
M_ClearMenusNoTitle(false);
demo.loadfiles = true;
demo.ignorefiles = false;

View file

@ -29,7 +29,7 @@ menuitem_t OPTIONS_Voice[] =
NULL, srb2::itemaction(&cv_voice_inputamp), 0, 0},
{IT_STRING | IT_CVAR, "Input Noise Suppression", "Suppress background noise from your voice.",
NULL, {.cvar = &cv_voice_denoise}, 0, 0},
NULL, srb2::itemaction(&cv_voice_denoise), 0, 0},
{IT_STRING | IT_CVAR, "Input Sensitivity", "Voice higher than this threshold will transmit, in decibels.",
NULL, srb2::itemaction(&cv_voice_activationthreshold), 0, 0 },

View file

@ -440,7 +440,7 @@ void M_HandleStaffReplay(INT32 choice)
staffbrief_t *staffbrief;
restoreMenu = &PLAY_TAReplayDef;
M_ClearMenus(true);
M_ClearMenusNoTitle(true);
demo.loadfiles = false;
demo.ignorefiles = true; // Just assume that record attack replays have the files needed
@ -507,7 +507,7 @@ void M_ReplayTimeAttack(INT32 choice)
restoreMenu = &PLAY_TAReplayDef;
M_ClearMenus(true);
M_ClearMenusNoTitle(true);
demo.loadfiles = false;
demo.ignorefiles = true; // Just assume that record attack replays have the files needed
@ -692,7 +692,7 @@ void M_StartTimeAttack(INT32 choice)
false
);
M_ClearMenus(true);
M_ClearMenusNoTitle(true);
G_UpdateTimeStickerMedals(levellist.choosemap, true);
}

View file

@ -292,7 +292,7 @@ void Obj_BulbTouched(mobj_t *special, mobj_t *toucher)
P_MoveOrigin(toucher, special->x, special->y, special->z);
toucher->player->nocontrol = 1;
P_SetTarget(&toucher->tracer, special);
toucher->flags &= ~MF_SHOOTABLE;
toucher->flags &= ~(MF_SHOOTABLE|MF_NOGRAVITY);
toucher->renderflags |= RF_DONTDRAW;
P_SetTarget(&special->target, toucher);
special->extravalue1 = spd;

View file

@ -11,6 +11,7 @@
#include "../doomdef.h"
#include "../doomstat.h"
#include "../info.h"
#include "../g_game.h"
#include "../k_objects.h"
#include "../p_local.h"
#include "../r_state.h"

View file

@ -215,18 +215,12 @@ boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
else
{
// Player Damage
if ((t1->type == MT_ORBINAUT_SHIELD || t1->type == MT_JAWZ_SHIELD)
&& !t2->player->invincibilitytimer && !K_IsBigger(t2, t1)) // UGH. Stumble ignores invinc. Fix this damage type someday.
{
P_DamageMobj(t2, t1, t1->target, 1, DMG_WOMBO | DMG_STUMBLE);
}
else
{
P_DamageMobj(t2, t1, t1->target, 1, DMG_WOMBO |
(tumbleitem ? DMG_TUMBLE : DMG_WIPEOUT));
if (tumbleitem || (gametyperules & GTR_SPHERES) || !t2->player->tripwireLeniency)
P_DamageMobj(t2, t1, t1->target, 1, DMG_WOMBO |
(tumbleitem ? DMG_TUMBLE : DMG_WIPEOUT));
if (tumbleitem || !t2->player->tripwireLeniency)
if ((gametyperules & GTR_SPHERES) || (t1->type != MT_ORBINAUT_SHIELD && t1->type != MT_JAWZ_SHIELD))
K_KartBouncing(t2, t1);
}
S_StartSound(t2, sfx_s3k7b);
}

View file

@ -220,6 +220,7 @@ void Obj_playerWPZTurbine(player_t *p)
}
mt = t->spawnpoint;
pmo->flags &= ~MF_NOGRAVITY;
opt1 = (mt->thing_args[0] != 0);

View file

@ -408,8 +408,15 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
case MT_FLOATINGITEM: // SRB2Kart
if (special->extravalue1 > 0 && toucher != special->tracer)
{
player->pflags |= PF_CASTSHADOW;
return;
if (special->tracer && !P_MobjWasRemoved(special->tracer) && special->tracer->player)
{
if (!G_SameTeam(special->tracer->player, player))
{
player->pflags |= PF_CASTSHADOW;
return;
}
}
}
if (special->threshold >= FIRSTPOWERUP)
@ -489,7 +496,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
if (special->fuse) // This box is respawning, but was broken very recently (see P_FuseThink)
{
// What was this box broken as?
if (!K_ThunderDome() && special->cvmem && !(special->flags2 & MF2_BOSSDEAD))
if (!K_ThunderDome() && special->cusval && !(special->flags2 & MF2_BOSSDEAD))
K_StartItemRoulette(player, false);
else
K_StartItemRoulette(player, true);
@ -497,12 +504,12 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
else if (specialstate >= S_RANDOMITEM1 && specialstate <= S_RANDOMITEM12)
{
K_StartItemRoulette(player, false);
special->cvmem = 1; // Lenient pickup should be ITEM
special->cusval = 1; // Lenient pickup should be ITEM
}
else
{
K_StartItemRoulette(player, true);
special->cvmem = 0; // Lenient pickup should be RING
special->cusval = 0; // Lenient pickup should be RING
}
P_ItemPop(special);
@ -2855,24 +2862,9 @@ static boolean P_FlashingException(const player_t *player, const mobj_t *inflict
return true;
}
/** Damages an object, which may or may not be a player.
* For melee attacks, source and inflictor are the same.
*
* \param target The object being damaged.
* \param inflictor The thing that caused the damage: creature, missile,
* gargoyle, and so forth. Can be NULL in the case of
* environmental damage, such as slime or crushing.
* \param source The creature or person responsible. For example, if a
* player is hit by a ring, the player who shot it. In some
* cases, the target will go after this object after
* receiving damage. This can be NULL.
* \param damage Amount of damage to be dealt.
* \param damagetype Type of damage to be dealt. If bit 7 (0x80) is set, this is an instant-kill.
* \return True if the target sustained damage, otherwise false.
* \todo Clean up this mess, split into multiple functions.
* \sa P_KillMobj
*/
boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
// P_DamageMobj for 0x0010 compat.
// I know this sucks ass, but this function is legitimately too complicated to add more behavior switches.
static boolean P_DamageMobjCompat(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
{
player_t *player;
player_t *playerInflictor;
@ -3671,6 +3663,857 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
return true;
}
/** Damages an object, which may or may not be a player.
* For melee attacks, source and inflictor are the same.
*
* \param target The object being damaged.
* \param inflictor The thing that caused the damage: creature, missile,
* gargoyle, and so forth. Can be NULL in the case of
* environmental damage, such as slime or crushing.
* \param source The creature or person responsible. For example, if a
* player is hit by a ring, the player who shot it. In some
* cases, the target will go after this object after
* receiving damage. This can be NULL.
* \param damage Amount of damage to be dealt.
* \param damagetype Type of damage to be dealt. If bit 7 (0x80) is set, this is an instant-kill.
* \return True if the target sustained damage, otherwise false.
* \todo Clean up this mess, split into multiple functions.
* \sa P_KillMobj
*/
boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
{
if (G_CompatLevel(0x0010))
return P_DamageMobjCompat(target, inflictor, source, damage, damagetype);
player_t *player;
player_t *playerInflictor;
boolean force = false;
boolean spbpop = false;
ATTRUNUSED boolean downgraded = false;
boolean truewhumble = false; // Invincibility-ignoring DMG_WHUMBLE from the Insta-Whip itself.
INT32 laglength = 6;
if (objectplacing)
return false;
if (target->health <= 0)
return false;
// Spectator handling
if (damagetype != DMG_SPECTATOR && target->player && target->player->spectator)
return false;
// source is checked without a removal guard in so many places that it's genuinely less work to do it here.
if (source && P_MobjWasRemoved(source))
source = NULL;
if (source && source->player && source->player->spectator)
return false;
if (((damagetype & DMG_TYPEMASK) == DMG_STING)
|| ((inflictor && !P_MobjWasRemoved(inflictor)) && inflictor->type == MT_BANANA && inflictor->health <= 1))
{
laglength = 2;
}
else if (target->type == MT_DROPTARGET || target->type == MT_DROPTARGET_SHIELD)
{
laglength = 0; // handled elsewhere
}
switch (target->type)
{
case MT_MONITOR:
damage = Obj_MonitorGetDamage(target, inflictor, damagetype);
Obj_MonitorOnDamage(target, inflictor, damage);
break;
case MT_CDUFO:
// Make it possible to pick them up during race
if (inflictor->type == MT_ORBINAUT_SHIELD || inflictor->type == MT_JAWZ_SHIELD)
return false;
break;
case MT_SPB:
spbpop = (damagetype & DMG_TYPEMASK) == DMG_VOLTAGE;
if (spbpop && source && source->player
&& source->player->roundconditions.spb_neuter == false)
{
source->player->roundconditions.spb_neuter = true;
source->player->roundconditions.checkthisframe = true;
}
break;
default:
break;
}
// Everything above here can't be forced.
{
UINT8 shouldForce = LUA_HookShouldDamage(target, inflictor, source, damage, damagetype);
if (P_MobjWasRemoved(target))
return (shouldForce == 1); // mobj was removed
if (shouldForce == 1)
force = true;
else if (shouldForce == 2)
return false;
}
switch (target->type)
{
case MT_BALLSWITCH_BALL:
Obj_BallSwitchDamaged(target, inflictor, source);
return false;
case MT_SA2_CRATE:
case MT_ICECAPBLOCK:
return Obj_TryCrateDamage(target, inflictor);
case MT_KART_LEFTOVER:
// intangible (do not let instawhip shred damage)
if (Obj_DestroyKart(target))
return false;
P_SetObjectMomZ(target, 12*FRACUNIT, false);
break;
default:
break;
}
if (!force)
{
if (!spbpop)
{
if (!(target->flags & MF_SHOOTABLE))
return false; // shouldn't happen...
}
}
if (target->flags2 & MF2_SKULLFLY)
target->momx = target->momy = target->momz = 0;
if (target->flags & (MF_ENEMY|MF_BOSS))
{
if (!force && target->flags2 & MF2_FRET) // Currently flashing from being hit
return false;
if (LUA_HookMobjDamage(target, inflictor, source, damage, damagetype) || P_MobjWasRemoved(target))
return true;
if (target->health > 1)
target->flags2 |= MF2_FRET;
}
player = target->player;
playerInflictor = inflictor ? inflictor->player : NULL;
if (playerInflictor)
{
AddTimesHit(playerInflictor);
}
if (player) // Player is the target
{
AddTimesHit(player);
if (player->pflags & PF_GODMODE)
return false;
if (!force)
{
// Player hits another player
if (source && source->player)
{
if (!P_PlayerHitsPlayer(target, inflictor, source, damage, damagetype))
return false;
}
}
if (source && source->player)
{
if (source->player->roundconditions.hit_midair == false
&& source != target
&& inflictor
&& K_IsMissileOrKartItem(inflictor)
&& target->player->airtime > TICRATE/2
&& source->player->airtime > TICRATE/2)
{
source->player->roundconditions.hit_midair = true;
source->player->roundconditions.checkthisframe = true;
}
if (source->player->roundconditions.hit_drafter_lookback == false
&& source != target
&& target->player->lastdraft == (source->player - players)
&& (K_GetKartButtons(source->player) & BT_LOOKBACK) == BT_LOOKBACK
/*&& (AngleDelta(K_MomentumAngle(source), R_PointToAngle2(source->x, source->y, target->x, target->y)) > ANGLE_90)*/)
{
source->player->roundconditions.hit_drafter_lookback = true;
source->player->roundconditions.checkthisframe = true;
}
if (source->player->roundconditions.giant_foe_shrunken_orbi == false
&& source != target
&& player->growshrinktimer > 0
&& !P_MobjWasRemoved(inflictor)
&& inflictor->type == MT_ORBINAUT
&& inflictor->scale < FixedMul((FRACUNIT + SHRINK_SCALE), mapobjectscale * 2)) // halfway between base scale and shrink scale, a little bit of leeway
{
source->player->roundconditions.giant_foe_shrunken_orbi = true;
source->player->roundconditions.checkthisframe = true;
}
if (source == target
&& !P_MobjWasRemoved(inflictor)
&& inflictor->type == MT_SPBEXPLOSION
&& inflictor->threshold == KITEM_EGGMAN
&& !P_MobjWasRemoved(inflictor->tracer)
&& inflictor->tracer != source
&& inflictor->tracer->player
&& inflictor->tracer->player->roundconditions.returntosender_mark == false)
{
inflictor->tracer->player->roundconditions.returntosender_mark = true;
inflictor->tracer->player->roundconditions.checkthisframe = true;
}
}
else if (!(inflictor && inflictor->player)
&& !(player->exiting || player->laps > numlaps)
&& damagetype != DMG_DEATHPIT)
{
// laps will never increment outside of GTR_CIRCUIT, so this is still fine
const UINT8 requiredbit = 1<<(player->laps & 7);
if (!(player->roundconditions.hittrackhazard[player->laps/8] & requiredbit))
{
player->roundconditions.hittrackhazard[player->laps/8] |= requiredbit;
player->roundconditions.checkthisframe = true;
}
}
// Instant-Death
if ((damagetype & DMG_DEATHMASK))
{
if (!P_KillPlayer(player, inflictor, source, damagetype))
return false;
}
else if (LUA_HookMobjDamage(target, inflictor, source, damage, damagetype))
{
return true;
}
else
{
UINT8 type = (damagetype & DMG_TYPEMASK);
const boolean hardhit = (type == DMG_EXPLODE || type == DMG_KARMA || type == DMG_TUMBLE); // This damage type can do evil stuff like ALWAYS combo
INT16 ringburst = 5;
if (inflictor && !P_MobjWasRemoved(inflictor) && inflictor->type == MT_INSTAWHIP && type == DMG_WHUMBLE)
truewhumble = true;
// Check if the player is allowed to be damaged!
// If not, then spawn the instashield effect instead.
if (!force)
{
boolean invincible = true;
boolean clash = true; // This effect is cool and reads well, why not
sfxenum_t sfx = sfx_None;
if (!(gametyperules & GTR_BUMPERS))
{
if (damagetype & DMG_STEAL)
{
// Gametype does not have bumpers, steal damage is intended to not do anything
// (No instashield is intentional)
return false;
}
}
if (player->invincibilitytimer > 0)
{
sfx = sfx_invind;
}
else if (K_IsBigger(target, inflictor) == true &&
// SPB bypasses grow (K_IsBigger handles NULL check)
(type != DMG_EXPLODE || inflictor->type != MT_SPBEXPLOSION || !inflictor->movefactor))
{
sfx = sfx_grownd;
}
else if (K_PlayerGuard(player))
{
sfx = sfx_s3k3a;
}
else if (player->overshield &&
(type != DMG_EXPLODE || inflictor->type != MT_SPBEXPLOSION || !inflictor->movefactor))
{
;
}
else if (player->lightningcharge &&
(type != DMG_EXPLODE || inflictor->type != MT_SPBEXPLOSION || !inflictor->movefactor))
{
;
sfx = sfx_s3k45;
}
else if (player->hyudorotimer > 0)
;
else
{
invincible = false;
}
// Hack for instawhip-guard counter, lets invincible players lose to guard
if (inflictor == target)
{
invincible = false;
}
if (player->pflags2 & PF2_ALWAYSDAMAGED)
{
invincible = false;
clash = false;
}
// TODO: doing this from P_DamageMobj limits punting to objects that damage the player.
// And it may be kind of yucky.
// But this is easier than accounting for every condition in PIT_CheckThing!
if (inflictor && K_PuntCollide(inflictor, target))
{
return false;
}
if (invincible && !truewhumble)
{
const INT32 oldHitlag = target->hitlag;
const INT32 oldHitlagInflictor = inflictor ? inflictor->hitlag : 0;
// Damage during hitlag should be a no-op
// for invincibility states because there
// are no flashing tics. If the damage is
// from a constant source, a deadlock
// would occur.
if (target->eflags & MFE_PAUSED)
{
player->timeshit--; // doesn't count
if (playerInflictor)
{
playerInflictor->timeshit--;
}
return false;
}
if (!clash) // Currently a no-op, damage floor hitlag kinda sucked ass
{
laglength = max(laglength / 2, 1);
K_SetHitLagForObjects(target, inflictor, source, laglength, false);
AddNullHitlag(player, oldHitlag);
AddNullHitlag(playerInflictor, oldHitlagInflictor);
}
if (player->timeshit > player->timeshitprev)
{
S_StartSound(target, sfx);
}
if (clash)
{
player->spheres = max(player->spheres - 5, 0);
if (inflictor)
{
K_DoPowerClash(target, inflictor);
if (player->lightningcharge)
{
K_SpawnDriftElectricSparks(player, SKINCOLOR_PURPLE, true);
}
if (inflictor->type == MT_SUPER_FLICKY)
{
Obj_BlockSuperFlicky(inflictor);
}
S_StartSound(target, sfx);
}
else if (source)
{
K_DoPowerClash(target, source);
S_StartSound(target, sfx);
}
}
// Full invulnerability
K_DoInstashield(player);
return false;
}
{
// Check if we should allow wombo combos (hard hits by default, inverted by the presence of DMG_WOMBO).
boolean allowcombo = ((hardhit || (type == DMG_STUMBLE || type == DMG_WHUMBLE)) == !(damagetype & DMG_WOMBO));
// Tumble/stumble is a special case.
if (type == DMG_TUMBLE)
{
// don't allow constant combo
if (player->tumbleBounces == 1 && (P_MobjFlip(target)*target->momz > 0))
allowcombo = false;
}
else if (type == DMG_STUMBLE || type == DMG_WHUMBLE)
{
// don't allow constant combo
if (player->tumbleBounces == TUMBLEBOUNCES-1 && (P_MobjFlip(target)*target->momz > 0))
{
if (type == DMG_STUMBLE)
return false; // No-sell strings of stumble
allowcombo = false;
}
}
if (inflictor && !P_MobjWasRemoved(inflictor) && inflictor->momx == 0 && inflictor->momy == 0 && inflictor->momz == 0 && inflictor->type != MT_SPBEXPLOSION)
{
// Probably a map hazard.
allowcombo = false;
}
if (allowcombo == false && (target->eflags & MFE_PAUSED))
{
return false;
}
// DMG_EXPLODE excluded from flashtic checks to prevent dodging eggbox/SPB with weak spinout
if ((target->hitlag == 0 || allowcombo == false) &&
player->flashing > 0 &&
type != DMG_EXPLODE &&
type != DMG_STUMBLE &&
type != DMG_WHUMBLE &&
P_FlashingException(player, inflictor) == false)
{
// Post-hit invincibility
K_DoInstashield(player);
return false;
}
else if (target->flags2 & MF2_ALREADYHIT) // do not deal extra damage in the same tic
{
K_SetHitLagForObjects(target, inflictor, source, laglength, true);
return false;
}
}
}
if (gametyperules & GTR_BUMPERS)
{
if (damagetype & DMG_STEAL)
{
// Steals 2 bumpers
damage = 2;
}
}
else
{
// Do not die from damage outside of bumpers health system
damage = 0;
}
boolean softenTumble = false;
// Sting and stumble shouldn't be rewarding Battle hits.
if (type == DMG_STING || type == DMG_STUMBLE)
{
damage = 0;
if (source && source != player->mo && source->player)
{
if (!P_PlayerInPain(player) && (player->defenseLockout || player->instaWhipCharge))
{
K_SpawnAmps(source->player, 20, target);
}
}
}
else
{
// We successfully damaged them! Give 'em some bumpers!
if (source && source != player->mo && source->player)
{
// Stone Shoe handles amps on its own, but this is also a good place to set soften tumble for it
if (inflictor->type == MT_STONESHOE || inflictor->type == MT_STONESHOE_CHAIN)
softenTumble = true;
else
K_SpawnAmps(source->player, K_PvPAmpReward((truewhumble) ? 30 : 20, source->player, player), target);
K_BotHitPenalty(player);
if (G_SameTeam(source->player, player))
{
if (type != DMG_EXPLODE)
{
type = DMG_STUMBLE;
downgraded = true;
}
}
else
{
for (UINT8 i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator || !players[i].mo || P_MobjWasRemoved(players[i].mo))
continue;
if (!G_SameTeam(source->player, &players[i]))
continue;
if (source->player == &players[i])
continue;
K_SpawnAmps(&players[i], FixedInt(FixedMul(5, K_TeamComebackMultiplier(player))), target);
}
}
// Extend the invincibility if the hit was a direct hit.
if (inflictor == source && source->player->invincibilitytimer &&
!K_PowerUpRemaining(source->player, POWERUP_SMONITOR))
{
tic_t kinvextend;
softenTumble = true;
if (gametyperules & GTR_CLOSERPLAYERS)
kinvextend = 2*TICRATE;
else
kinvextend = 3*TICRATE;
// Reduce the value of subsequent invinc extensions
kinvextend = kinvextend / (1 + source->player->invincibilityextensions); // 50%, 33%, 25%[...]
kinvextend = max(kinvextend, TICRATE);
source->player->invincibilityextensions++;
source->player->invincibilitytimer += kinvextend;
if (P_IsDisplayPlayer(source->player))
S_StartSound(NULL, sfx_gsha7);
}
// if the inflictor is a landmine, its reactiontime will be non-zero if it is still moving
if (inflictor->type == MT_LANDMINE && inflictor->reactiontime > 0)
{
// reduce tumble severity to account for getting beaned point blank sometimes
softenTumble = true;
// make it more consistent with set landmines
inflictor->momx = 0;
inflictor->momy = 0;
}
K_TryHurtSoundExchange(target, source);
if (K_Cooperative() == false)
{
K_BattleAwardHit(source->player, player, inflictor, damage);
}
if (K_Bumpers(source->player) < K_StartingBumperCount() || (damagetype & DMG_STEAL))
{
K_TakeBumpersFromPlayer(source->player, player, damage);
}
if (damagetype & DMG_STEAL)
{
// Give them ALL of your emeralds instantly :)
source->player->emeralds |= player->emeralds;
player->emeralds = 0;
K_CheckEmeralds(source->player);
}
}
if (!(damagetype & DMG_STEAL))
{
// Drop all of your emeralds
K_DropEmeraldsFromPlayer(player, player->emeralds);
}
}
if (source && source != player->mo && source->player)
{
if (damagetype != DMG_DEATHPIT)
{
player->pitblame = source->player - players;
}
}
player->sneakertimer = player->numsneakers = 0;
player->panelsneakertimer = player->numpanelsneakers = 0;
player->weaksneakertimer = player->numweaksneakers = 0;
player->driftboost = player->strongdriftboost = 0;
player->gateBoost = 0;
player->fastfall = 0;
player->ringboost = 0;
player->glanceDir = 0;
player->preventfailsafe = TICRATE*3;
player->pflags &= ~PF_GAINAX;
Obj_EndBungee(player);
K_BumperInflate(target->player);
UINT32 hurtskinflags = (demo.playback)
? demo.skinlist[demo.currentskinid[(player-players)]].flags
: skins[player->skin]->flags;
if (hurtskinflags & SF_IRONMAN)
{
if (gametyperules & GTR_BUMPERS)
SetRandomFakePlayerSkin(player, false, true);
}
// Explosions are explicit combo setups.
if (damagetype & DMG_EXPLODE)
player->bumperinflate = 0;
if (player->spectator == false && !(player->charflags & SF_IRONMAN))
{
UINT32 skinflags = (demo.playback)
? demo.skinlist[demo.currentskinid[(player-players)]].flags
: skins[player->skin]->flags;
if (skinflags & SF_IRONMAN)
{
player->mo->skin = skins[player->skin];
player->charflags = skinflags;
K_SpawnMagicianParticles(player->mo, 5);
}
}
if (player->rings <= -20)
{
player->markedfordeath = true;
damagetype = DMG_TUMBLE;
type = DMG_TUMBLE;
P_StartQuakeFromMobj(5, 44 * player->mo->scale, 2560 * player->mo->scale, player->mo);
//P_KillPlayer(player, inflictor, source, damagetype);
}
// Death save! On your last hit, no matter what, demote to weakest damage type for one last escape chance.
if (player->mo->health == 2 && damage && gametyperules & GTR_BUMPERS)
{
K_AddMessageForPlayer(player, "\x8DLast Chance!", false, false);
S_StartSound(target, sfx_gshc7);
player->flashing = TICRATE;
type = DMG_STUMBLE;
downgraded = true;
}
// Downgrade backthrown items that are not dedicated traps.
if (inflictor && !P_MobjWasRemoved(inflictor) && P_IsKartItem(inflictor->type) && inflictor->cvmem
&& inflictor->type != MT_BANANA)
{
type = DMG_WHUMBLE;
downgraded = true;
}
// Downgrade orbital items.
if (inflictor && !P_MobjWasRemoved(inflictor) && (inflictor->type == MT_ORBINAUT_SHIELD || inflictor->type == MT_JAWZ_SHIELD))
{
type = DMG_WHUMBLE;
downgraded = true;
}
if (!(gametyperules & GTR_SPHERES) && player->tripwireLeniency && !P_PlayerInPain(player))
{
switch (type)
{
case DMG_EXPLODE:
type = DMG_TUMBLE;
downgraded = true;
softenTumble = true;
break;
case DMG_TUMBLE:
softenTumble = true;
break;
case DMG_NORMAL:
case DMG_WIPEOUT:
downgraded = true;
type = DMG_WHUMBLE;
break;
default:
break;
}
}
switch (type)
{
case DMG_STING:
K_DebtStingPlayer(player, source);
K_KartPainEnergyFling(player);
ringburst = 0;
break;
case DMG_STUMBLE:
case DMG_WHUMBLE:
K_StumblePlayer(player);
ringburst = (type == DMG_WHUMBLE) ? 5 : 0;
break;
case DMG_TUMBLE:
K_TumblePlayer(player, inflictor, source, softenTumble);
ringburst = 10;
break;
case DMG_EXPLODE:
case DMG_KARMA:
ringburst = K_ExplodePlayer(player, inflictor, source);
break;
case DMG_WIPEOUT:
K_SpinPlayer(player, inflictor, source, KSPIN_WIPEOUT);
K_KartPainEnergyFling(player);
break;
case DMG_VOLTAGE:
case DMG_NORMAL:
default:
K_SpinPlayer(player, inflictor, source, KSPIN_SPINOUT);
break;
}
// Have a shield? You get hit, but don't lose your rings!
if (player->curshield != KSHIELD_NONE)
{
ringburst = 0;
}
player->ringburst += ringburst;
if (type != DMG_STUMBLE)
{
if (type != DMG_STING)
player->flashing = K_GetKartFlashing(player);
K_PopPlayerShield(player);
player->instashield = 15;
K_PlayPainSound(target, source);
player->ringboost = 0;
}
if (gametyperules & GTR_BUMPERS)
player->spheres = min(player->spheres + 10, 40);
if ((hardhit == true && !softenTumble) || cv_kartdebughuddrop.value)
{
K_DropItems(player);
}
else
{
K_DropHnextList(player);
}
if (inflictor && !P_MobjWasRemoved(inflictor) && inflictor->type == MT_BANANA)
{
player->flipDI = true;
}
// Apply stun!
if (type != DMG_STING)
{
K_ApplyStun(player, inflictor, source, damage, damagetype);
}
K_DefensiveOverdrive(target->player);
}
}
else
{
if (target->type == MT_SPECIAL_UFO)
{
return Obj_SpecialUFODamage(target, inflictor, source, damagetype);
}
else if (target->type == MT_BLENDEYE_MAIN)
{
VS_BlendEye_Damage(target, inflictor, source, damage);
}
if (damagetype & DMG_STEAL)
{
// Not a player, steal damage is intended to not do anything
return false;
}
if ((target->flags & MF_BOSS) == MF_BOSS)
{
targetdamaging_t targetdamaging = UFOD_GENERIC;
if (P_MobjWasRemoved(inflictor) == true)
;
else switch (inflictor->type)
{
case MT_GACHABOM:
targetdamaging = UFOD_GACHABOM;
break;
case MT_ORBINAUT:
case MT_ORBINAUT_SHIELD:
targetdamaging = UFOD_ORBINAUT;
break;
case MT_BANANA:
targetdamaging = UFOD_BANANA;
break;
case MT_INSTAWHIP:
inflictor->extravalue2 = 1; // Disable whip collision
targetdamaging = UFOD_WHIP;
break;
case MT_PLAYER:
targetdamaging = UFOD_BOOST;
break;
case MT_JAWZ:
case MT_JAWZ_SHIELD:
targetdamaging = UFOD_JAWZ;
break;
case MT_SPB:
targetdamaging = UFOD_SPB;
break;
default:
break;
}
P_TrackRoundConditionTargetDamage(targetdamaging);
}
}
// do the damage
if (damagetype & DMG_DEATHMASK)
target->health = 0;
else
target->health -= damage;
if (source && source->player && target)
G_GhostAddHit((INT32) (source->player - players), target);
// Insta-Whip (DMG_WHUMBLE): do not reduce hitlag because
// this can leave room for double-damage.
if (truewhumble && (gametyperules & GTR_BUMPERS) && !battleprisons)
laglength /= 2;
if (target->type == MT_PLAYER && inflictor && !P_MobjWasRemoved(inflictor)
&& inflictor->type == MT_PLAYER && K_PlayerCanPunt(inflictor->player))
laglength = max(laglength / 2, 2);
if (!(target->player && (damagetype & DMG_DEATHMASK)))
K_SetHitLagForObjects(target, inflictor, source, laglength, true);
target->flags2 |= MF2_ALREADYHIT;
if (target->health <= 0)
{
P_KillMobj(target, inflictor, source, damagetype);
return true;
}
//K_SetHitLagForObjects(target, inflictor, source, laglength, true);
if (!player)
{
P_SetMobjState(target, target->info->painstate);
if (!P_MobjWasRemoved(target))
{
// if not intent on another player,
// chase after this one
P_SetTarget(&target->target, source);
}
}
return true;
}
#define RING_LAYER_SIDE_SIZE (3)
#define RING_LAYER_SIZE (RING_LAYER_SIDE_SIZE * 2)

View file

@ -533,8 +533,8 @@ struct BasicFF_t
#define DMG_STING 0x04
#define DMG_KARMA 0x05 // Karma Bomb explosion -- works like DMG_EXPLODE, but steals half of their bumpers & deletes the rest
#define DMG_VOLTAGE 0x06
#define DMG_STUMBLE 0x07 // Does not award points in Battle
#define DMG_WHUMBLE 0x08 // <-- But this one DOES!
#define DMG_STUMBLE 0x07 // Harmless disruption.
#define DMG_WHUMBLE 0x08 // Harmful disruption. (Awards points, strips rings, pops shields, etc)
//// Death types - cannot be combined with damage types
#define DMG_INSTAKILL 0x80
#define DMG_DEATHPIT 0x81

View file

@ -4104,8 +4104,6 @@ papercollision:
static void P_BouncePlayerMove(mobj_t *mo, TryMoveResult_t *result)
{
extern consvar_t cv_showgremlins;
fixed_t mmomx = 0, mmomy = 0;
fixed_t oldmomx = mo->momx, oldmomy = mo->momy;
@ -4130,24 +4128,6 @@ static void P_BouncePlayerMove(mobj_t *mo, TryMoveResult_t *result)
slidemo = mo;
bestslideline = result->line;
if (bestslideline == NULL && cv_showgremlins.value)
{
// debug
mobj_t*x = P_SpawnMobj(mo->x, mo->y, mo->z, MT_THOK);
x->frame = FF_FULLBRIGHT | FF_ADD;
x->renderflags = RF_ALWAYSONTOP;
x->color = SKINCOLOR_RED;
CONS_Printf(
"GREMLIN: leveltime=%u x=%f y=%f z=%f angle=%f\n",
leveltime,
FixedToFloat(mo->x),
FixedToFloat(mo->y),
FixedToFloat(mo->z),
AngleToFloat(R_PointToAngle2(0, 0, oldmomx, oldmomy))
);
}
if (mo->health <= 0)
{
tmxmove = mo->momx;

View file

@ -7997,7 +7997,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
}
}
break;
case MT_TRIPWIREAPPROACH: {
case MT_TRIPWIREAPPROACH: { // Subsonic Visuals
if (!mobj->target || !mobj->target->health || !mobj->target->player)
{
P_RemoveMobj(mobj);
@ -8007,17 +8007,53 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
mobj_t *target = mobj->target;
player_t *player = target->player;
fixed_t myspeed = (player->speed);
boolean In_A_Race = ((gametyperules & GTR_CIRCUIT) && !K_Cooperative() && M_NotFreePlay() && !modeattacking); // If you're in a real race.
boolean prorated_sonicboom_alert = (K_PlayerTripwireSpeedThreshold(player) > 2 * K_GetKartSpeed(player, false, false)) ; // If you're being prorated.
fixed_t maxspeed = K_PlayerTripwireSpeedThreshold(player); // Centered at this speed.
fixed_t minspeed = max(2 * maxspeed / 4, 16 * K_GetKartSpeed(player, false, false) / 10); // Starts appearing at this speed.
fixed_t minspeed = max(2 * maxspeed / 4, 7 * K_GetKartSpeed(player, false, false) / 5); // Starts appearing at this speed.
fixed_t alertspeed = 9 * maxspeed / 10; // When to flash?
fixed_t frontoffset = 5*target->scale; // How far in front?
fixed_t percentvisible = 0;
if (myspeed > minspeed)
{
percentvisible = min(FRACUNIT, FixedDiv(myspeed - minspeed, maxspeed - minspeed));
}
if (myspeed >= maxspeed || player->tripwireLeniency)
percentvisible = 0;
{
player->subsonicleniency++; // Subsonic visual stays for a bit during tripwire leniency
if(player->subsonicleniency == 1 && player->tripwireLeniency && myspeed >= maxspeed && !S_SoundPlaying(player->mo, sfx_gsha7)) // Don't play during superring too
{
mobj_t *boost = P_SpawnMobjFromMobj(player->mo, 0, 0, player->mo->height/2, MT_SONICBOOM);
boost->momx = player->mo->momx/2;
boost->momy = player->mo->momy/2;
boost->momz = player->mo->momz/2;
boost->angle = player->mo->angle + ANGLE_90;
boost->scalespeed = boost->scale;
boost->destscale = boost->scale*8;
//sonicboom->color = SKINCOLOR_WHITE;
boost->fuse = 8;
}
}
else
{
player->subsonicleniency = 0; // Goes back down otherwise
}
if (player->subsonicleniency >= (3*TICRATE))
{
percentvisible = 0; // Once it stays long enough, no longer visible
}
#if 0
if (!K_PlayerUsesBotMovement(player))
{
CONS_Printf("SSL=%d, PV=%d\n", player->subsonicleniency, percentvisible);
}
#endif
#if 0
fixed_t hang = 85*FRACUNIT/100; // Dampen inward movement past a certain point
@ -8045,8 +8081,10 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
P_InstaScale(mobj, FixedMul(target->scale, easedscale));
K_MatchGenericExtraFlagsNoInterp(mobj, target);
UINT8 maxtranslevel = NUMTRANSMAPS - 2;
UINT8 maxtranslevel = NUMTRANSMAPS;
UINT8 trans = FixedInt(FixedMul(percentvisible, FRACUNIT*(maxtranslevel+1)));
//UINT8 trans = FixedInt(FixedMul(percentvisible - player->subsonicleniency * FRACUNIT/100, FRACUNIT*(maxtranslevel+1)));
if (trans > maxtranslevel)
trans = maxtranslevel;
trans = NUMTRANSMAPS - trans;
@ -8060,13 +8098,52 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
mobj->renderflags |= RF_PAPERSPRITE;
mobj->colorized = true;
if (myspeed > alertspeed)
mobj->color = (leveltime & 1) ? SKINCOLOR_LILAC : SKINCOLOR_JAWZ;
else
mobj->color = SKINCOLOR_WHITE;
/*
if (!K_PlayerUsesBotMovement(player))
{
CONS_Printf("In_A_Race=%d, Prorated_SonicBoom_Alert=%d\n", In_A_Race, prorated_sonicboom_alert);
}
*/
if (In_A_Race == true && prorated_sonicboom_alert == true)
{
mobj->color = (leveltime & 1) ? SKINCOLOR_KETCHUP : SKINCOLOR_RED; // If you're being prorated we flash red
trans = trans*2;
}
else if (myspeed > alertspeed)
mobj->color = (leveltime & 1) ? SKINCOLOR_LILAC : SKINCOLOR_JAWZ; // If the Subsonic lines meet we flash tripwire colors
else
mobj->color = SKINCOLOR_WHITE; // Default
mobj->renderflags |= (RF_DONTDRAW & ~K_GetPlayerDontDrawFlag(player));
// Alright, let's just handle all the sfx down here
if (P_IsDisplayPlayer(player))
{
UINT8 MIN_VOLUME = 25;
UINT8 MAX_VOLUME = 75;
UINT8 volume = FixedRescale(myspeed, minspeed, maxspeed, Easing_Linear, MIN_VOLUME, MAX_VOLUME);
if (myspeed >= minspeed && myspeed < maxspeed)
{
S_StopSoundByID(mobj, sfx_sonbo1);
if(!S_SoundPlaying(mobj, sfx_sonbo3))
S_StartSoundAtVolume(mobj, sfx_sonbo3, volume); // Subsonic SFX
}
else if (myspeed >= maxspeed || player->tripwireLeniency)
{
S_StopSoundByID(mobj, sfx_sonbo3);
if(!S_SoundPlaying(mobj, sfx_sonbo1))
S_StartSoundAtVolume(mobj, sfx_sonbo1, MAX_VOLUME); // SonicBoom lingering SFX
}
else
{
S_StopSoundByID(mobj, sfx_sonbo1);
S_StopSoundByID(mobj, sfx_sonbo3);
}
}
break;
}
case MT_TRIPWIREBOOST: {
@ -9476,8 +9553,10 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
// it's still necessary to refresh SPR2 on skin changes.
P_SetMobjState(cur, (newperfect == true) ? S_KART_SIGL : S_KART_SIGN);
if (cv_shittysigns.value && cur->state != &states[S_KART_SIGL])
cur->sprite2 = P_GetSkinSprite2(skins[newplayer->skin], SPR2_SSIG, NULL);;
if ((cv_shittysigns.value || newplayer->mfdfinish) && cur->state != &states[S_KART_SIGL])
{
cur->sprite2 = P_GetSkinSprite2(skins[newplayer->skin], SPR2_SSIG, NULL);
}
}
}
else if (cur->state == &states[S_SIGN_ERROR])

View file

@ -512,6 +512,7 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEUINT16(save->p, players[i].spindash);
WRITEFIXED(save->p, players[i].spindashspeed);
WRITEUINT8(save->p, players[i].spindashboost);
WRITEUINT8(save->p, players[i].ringboostinprogress);
WRITEFIXED(save->p, players[i].fastfall);
WRITEFIXED(save->p, players[i].fastfallBase);
@ -533,6 +534,7 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEUINT16(save->p, players[i].tripwireLeniency);
WRITEUINT8(save->p, players[i].tripwireAirLeniency);
WRITEUINT8(save->p, players[i].fakeBoost);
WRITEUINT16(save->p, players[i].subsonicleniency);
WRITESINT8(save->p, players[i].itemtype);
WRITEUINT8(save->p, players[i].itemamount);
@ -653,7 +655,6 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEFIXED(save->p, players[i].overdrivepower);
WRITEUINT8(save->p, players[i].overdriveready);
WRITEUINT8(save->p, players[i].overdrivelenient);
WRITEUINT16(save->p, players[i].speedpunt);
WRITEUINT16(save->p, players[i].trickcharge);
WRITEUINT16(save->p, players[i].infinitether);
@ -700,6 +701,7 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEUINT8(save->p, players[i].analoginput);
WRITEUINT8(save->p, players[i].markedfordeath);
WRITEUINT8(save->p, players[i].mfdfinish);
WRITEUINT8(save->p, players[i].dotrickfx);
WRITEUINT8(save->p, players[i].stingfx);
WRITEUINT8(save->p, players[i].bumperinflate);
@ -1191,6 +1193,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
players[i].spindash = READUINT16(save->p);
players[i].spindashspeed = READFIXED(save->p);
players[i].spindashboost = READUINT8(save->p);
players[i].ringboostinprogress = READUINT8(save->p);
players[i].fastfall = READFIXED(save->p);
players[i].fastfallBase = READFIXED(save->p);
@ -1212,6 +1215,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
players[i].tripwireLeniency = READUINT16(save->p);
players[i].tripwireAirLeniency = READUINT8(save->p);
players[i].fakeBoost = READUINT8(save->p);
players[i].subsonicleniency = READUINT16(save->p);
players[i].itemtype = READSINT8(save->p);
players[i].itemamount = READUINT8(save->p);
@ -1331,7 +1335,6 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
players[i].overdrivepower = READFIXED(save->p);
players[i].overdriveready = READUINT8(save->p);
players[i].overdrivelenient = READUINT8(save->p);
players[i].speedpunt = READUINT16(save->p);
players[i].trickcharge = READUINT16(save->p);
players[i].infinitether = READUINT16(save->p);
@ -1377,6 +1380,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
players[i].analoginput = READUINT8(save->p);
players[i].markedfordeath = READUINT8(save->p);
players[i].mfdfinish = READUINT8(save->p);
players[i].dotrickfx = READUINT8(save->p);
players[i].stingfx = READUINT8(save->p);
players[i].bumperinflate = READUINT8(save->p);

View file

@ -1964,7 +1964,7 @@ static void K_HandleLapIncrement(player_t *player)
if (!G_TimeAttackStart() && leveltime < starttime && !(gametyperules & GTR_ROLLINGSTART))
{
// freeze 'em until fault penalty is over
player->mo->hitlag = starttime - leveltime + TICRATE;
player->mo->hitlag = starttime - leveltime + 2*TICRATE;
P_ResetPlayer(player);
player->pflags |= PF_VOID;
player->mo->renderflags |= RF_DONTDRAW;
@ -2020,7 +2020,7 @@ static void K_HandleLapIncrement(player_t *player)
}
// finished race exit setup
if (player->laps > numlaps && !K_InRaceDuel())
if (player->laps > numlaps)
{
pflags_t applyflags = 0;
if (specialstageinfo.valid == true)
@ -5421,11 +5421,22 @@ static void P_EvaluateDamageType(player_t *player, sector_t *sector, boolean isT
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_INSTAKILL);
break;
case SD_STUMBLE:
if (isTouching)
if (isTouching && G_CompatLevel(0x0010))
{
player->pflags2 |= PF2_ALWAYSDAMAGED;
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_STUMBLE);
player->pflags2 &= ~PF2_ALWAYSDAMAGED;
}
else
{
if (isTouching && player->mo->hitlag == 0 &&
(player->mo->momz == 0 || (player->mo->momz > 0) != (P_MobjFlip(player->mo) > 0)))
{
player->pflags2 |= PF2_ALWAYSDAMAGED;
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_STUMBLE);
player->pflags2 &= ~PF2_ALWAYSDAMAGED;
}
}
break;

View file

@ -15,6 +15,8 @@
#include "p_sweep.hpp"
#include "p_local.h"
#include "g_demo.h"
#include "r_main.h"
namespace
{
@ -30,6 +32,8 @@ void P_TestLine(line_t* ld)
line_t* P_SweepTestLines(fixed_t ax, fixed_t ay, fixed_t bx, fixed_t by, fixed_t r, vector2_t* return_normal)
{
extern consvar_t cv_showgremlins;
using namespace srb2::math;
using namespace srb2::sweep;
@ -60,13 +64,65 @@ line_t* P_SweepTestLines(fixed_t ax, fixed_t ay, fixed_t bx, fixed_t by, fixed_t
}
}
g_lines.clear();
if (!collision)
{
return nullptr;
line_t *line = nullptr;
if (!g_lines.empty() && !G_CompatLevel(0x000E))
{
// FIXME: This condition is a failsafe!
// SlopeAABBvsLine::vs_slope can sometimes report
// no collision despite P_CheckPosition saying otherwise.
// (Generally related to infinitesimal numbers or overflows.)
// When it reports no collision, that means no line and
// no normals to base the collision off of.
// Here we provide the last line checked and normals based on
// the line and the player's momentum angle.
// But the proper fix would be to make vs_slope work!!
line = g_lines.back();
angle_t lineangle = line->angle;
angle_t mobjangle = R_PointToAngle2(ax, ay, bx, by);
angle_t diff = lineangle - mobjangle;
lineangle += diff > ANGLE_180 ? -ANGLE_90 : ANGLE_90;
return_normal->x = FINECOSINE((lineangle >> ANGLETOFINESHIFT) & FINEMASK);
return_normal->y = FINESINE((lineangle >> ANGLETOFINESHIFT) & FINEMASK);
// Moving the gremlin debug here so that it
// still fires even when the workaround is used.
if (cv_showgremlins.value)
{
mobj_t *mo = g_tm.thing;
if (mo)
{
mobj_t *x = P_SpawnMobj(mo->x, mo->y, mo->z, MT_THOK);
x->frame = FF_FULLBRIGHT | FF_ADD;
x->renderflags = RF_ALWAYSONTOP;
x->color = SKINCOLOR_RED;
CONS_Printf(
"GREMLIN: leveltime=%u x=%f y=%f z=%f momx=%f momy=%f momz=%f\n",
leveltime,
FixedToFloat(mo->x),
FixedToFloat(mo->y),
FixedToFloat(mo->z),
FixedToFloat(mo->momx),
FixedToFloat(mo->momy),
FixedToFloat(mo->momz)
);
}
}
}
g_lines.clear();
return line;
}
g_lines.clear();
return_normal->x = Fixed {collision->normal.x};
return_normal->y = Fixed {collision->normal.y};

View file

@ -1272,6 +1272,7 @@ void P_DoPlayerExit(player_t *player, pflags_t flags)
if (!player->spectator && (gametyperules & GTR_CIRCUIT)) // Special Race-like handling
{
K_UpdateAllPlayerPositions();
player->mfdfinish = player->markedfordeath;
}
if (!(gametyperules & GTR_SPHERES) && (player->pflags & PF_RINGLOCK) && grandprixinfo.gp)

View file

@ -732,8 +732,7 @@ void R_RenderMaskedSegRange(drawseg_t *drawseg, INT32 x1, INT32 x2)
template <typename T>
static constexpr T saturating_add(T x, T y) noexcept
{
INT64 z;
z = static_cast<INT64>(x) + static_cast<INT64>(y);
INT64 z = static_cast<INT64>(x) + static_cast<INT64>(y);
if (z > static_cast<INT64>(std::numeric_limits<T>::max()))
{
z = static_cast<INT64>(std::numeric_limits<T>::max());
@ -748,8 +747,7 @@ static constexpr T saturating_add(T x, T y) noexcept
template <typename T>
static constexpr T saturating_mul(T x, T y) noexcept
{
INT64 z;
z = static_cast<INT64>(x) * static_cast<INT64>(y);
INT64 z = static_cast<INT64>(x) * static_cast<INT64>(y);
if (z > static_cast<INT64>(std::numeric_limits<T>::max()))
{
z = static_cast<INT64>(std::numeric_limits<T>::max());

View file

@ -1293,7 +1293,7 @@ Rloadtextures (INT32 i, INT32 w)
}
INT32 sizeLimit = 2048;
if (w <= mainwads)
if (w < mainwads)
{
// TODO: we ran out of time to do this properly.
// 4096 limit on textures may be incompatible with some older graphics cards (circa 2005-2008?).

View file

@ -970,7 +970,7 @@ void S_UpdateVoicePositionalProperties(void)
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || !voice_proximity_enabled)
if (!playeringame[i])
{
I_SetPlayerVoiceProperties(i, speakingplayerattenuation, 0.0f);
continue;
@ -1025,7 +1025,7 @@ void S_UpdateVoicePositionalProperties(void)
attenuation_distance = voice_distanceattenuation_teamdistance;
}
if (attenuation_distance > 0 && voice_distanceattenuation_factor >= 0 && voice_distanceattenuation_factor <= 1.0f)
if (voice_proximity_enabled && attenuation_distance > 0 && voice_distanceattenuation_factor >= 0 && voice_distanceattenuation_factor <= 1.0f)
{
float invfactor = 1.0f - voice_distanceattenuation_factor;
float distfactor = max(0.f, min(attenuation_distance, pdistance)) / attenuation_distance;

View file

@ -20,7 +20,6 @@
#include "../core/string.h"
#include <exception>
#include <stdexcept>
#include <tracy/tracy/Tracy.hpp>
@ -32,11 +31,7 @@
#include <errno.h>
#endif
extern "C" {
#include "time.h" // For log timestamps
}
#ifdef HAVE_SDL
#ifdef HAVE_TTF
#include "SDL.h"
@ -68,46 +63,6 @@ char logfilename[1024];
#endif
#endif
#if defined (_WIN32)
extern "C" {
#include "../win32/win_dbg.h"
}
typedef BOOL (WINAPI *p_IsDebuggerPresent)(VOID);
#endif
#if defined (_WIN32)
static inline VOID MakeCodeWritable(VOID)
{
#ifdef USEASM // Disable write-protection of code segment
DWORD OldRights;
const DWORD NewRights = PAGE_EXECUTE_READWRITE;
PBYTE pBaseOfImage = (PBYTE)GetModuleHandle(NULL);
PIMAGE_DOS_HEADER dosH =(PIMAGE_DOS_HEADER)pBaseOfImage;
PIMAGE_NT_HEADERS ntH = (PIMAGE_NT_HEADERS)(pBaseOfImage + dosH->e_lfanew);
PIMAGE_OPTIONAL_HEADER oH = (PIMAGE_OPTIONAL_HEADER)
((PBYTE)ntH + sizeof (IMAGE_NT_SIGNATURE) + sizeof (IMAGE_FILE_HEADER));
LPVOID pA = pBaseOfImage+oH->BaseOfCode;
SIZE_T pS = oH->SizeOfCode;
#if 1 // try to find the text section
PIMAGE_SECTION_HEADER ntS = IMAGE_FIRST_SECTION (ntH);
WORD s;
for (s = 0; s < ntH->FileHeader.NumberOfSections; s++)
{
if (memcmp (ntS[s].Name, ".text\0\0", 8) == 0)
{
pA = pBaseOfImage+ntS[s].VirtualAddress;
pS = ntS[s].Misc.VirtualSize;
break;
}
}
#endif
if (!VirtualProtect(pA,pS,NewRights,&OldRights))
I_Error("Could not make code writable\n");
#endif
}
#endif
#ifdef LOGMESSAGES
static void InitLogging(void)
{
@ -212,10 +167,6 @@ static void init_exchndl()
if (pfnExcHndlInit != NULL)
(pfnExcHndlInit)();
}
#else
static void init_exchndl()
{
}
#endif
#ifdef _WIN32
@ -300,25 +251,12 @@ int main(int argc, char **argv)
//I_OutputMsg("I_StartupSystem() ...\n");
I_StartupSystem();
#if defined (_WIN32)
#if defined (_WIN32) && !defined(_MSC_VER)
if (!M_CheckParm("-noexchndl"))
{
#if 0 // just load the DLL
p_IsDebuggerPresent pfnIsDebuggerPresent = (p_IsDebuggerPresent)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsDebuggerPresent");
if ((!pfnIsDebuggerPresent || !pfnIsDebuggerPresent())
#ifdef BUGTRAP
&& !InitBugTrap()
#endif
)
#endif
{
init_exchndl();
}
init_exchndl();
}
#ifndef __MINGW32__
prevExceptionFilter = SetUnhandledExceptionFilter(RecordExceptionInfo);
#endif
MakeCodeWritable();
#endif
try {
@ -353,8 +291,6 @@ int main(int argc, char **argv)
return 0;
}
#endif
#ifdef _MSC_VER
int WINAPI WinMain(HINSTANCE pInstance, HINSTANCE pPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{

View file

@ -39,6 +39,12 @@ typedef DWORD (WINAPI *p_timeGetTime) (void);
typedef UINT (WINAPI *p_timeEndPeriod) (UINT);
typedef HANDLE (WINAPI *p_OpenFileMappingA) (DWORD, BOOL, LPCSTR);
typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T);
#if defined(_WIN32) && !defined(__GNUC__)
#define USE_DBGHELP
#include <DbgHelp.h>
#endif
#endif
#include <stdio.h>
#include <stdlib.h>
@ -141,6 +147,10 @@ typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T);
#define UNIXBACKTRACE
#endif
#ifdef HAVE_CPPTRACE
#include <cpptrace/cpptrace.hpp>
#endif
// Locations for searching for bios.pk3
#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)
#define DEFAULTWADLOCATION1 "/usr/local/share/games/RingRacers"
@ -302,8 +312,10 @@ static void I_ShowErrorMessageBox(const char *messagefordevelopers, boolean dump
dumpmade ?
#if defined (UNIXBACKTRACE)
"crash-log.txt"
#elif defined (_WIN32)
#elif defined (_WIN32) && defined(__GNUC__)
".rpt crash dump"
#elif defined (USE_DBGHELP)
".dmp crash dump"
#endif
" (very important!) and " : "",
#ifdef LOGMESSAGES
@ -369,12 +381,83 @@ static void I_ShowErrorMessageBox(const char *messagefordevelopers, boolean dump
// in case the fullscreen window blocks it for some absurd reason.
}
static void I_ReportSignal(int num, int coredumped)
static void I_ReportSignal(int num, int coredumped, void* tracefromcpptrace)
{
//static char msg[] = "oh no! back to reality!\r\n";
const char * sigmsg;
char msg[128];
char msg[8192];
#ifdef USE_DBGHELP
// The signal code is a WIN32 exception code, not a libc signal
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-exception_record
switch (num)
{
case EXCEPTION_ACCESS_VIOLATION:
sigmsg = "EXCEPTION_ACCESS_VIOLATION";
break;
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
sigmsg = "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
break;
case EXCEPTION_BREAKPOINT:
sigmsg = "EXCEPTION_BREAKPOINT";
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
sigmsg = "EXCEPTION_DATATYPE_MISALIGNMENT";
break;
case EXCEPTION_FLT_DENORMAL_OPERAND:
sigmsg = "EXCEPTION_FLT_DENORMAL_OPERAND";
break;
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
sigmsg = "EXCEPTION_FLT_DENORMAL_OPERAND";
break;
case EXCEPTION_FLT_INEXACT_RESULT:
sigmsg = "EXCEPTION_FLT_INEXACT_RESULT";
break;
case EXCEPTION_FLT_INVALID_OPERATION:
sigmsg = "EXCEPTION_FLT_INVALID_OPERATION";
break;
case EXCEPTION_FLT_OVERFLOW:
sigmsg = "EXCEPTION_FLT_OVERFLOW";
break;
case EXCEPTION_FLT_STACK_CHECK:
sigmsg = "EXCEPTION_FLT_STACK_CHECK";
break;
case EXCEPTION_FLT_UNDERFLOW:
sigmsg = "EXCEPTION_FLT_UNDERFLOW";
break;
case EXCEPTION_ILLEGAL_INSTRUCTION:
sigmsg = "EXCEPTION_ILLEGAL_INSTRUCTION";
break;
case EXCEPTION_IN_PAGE_ERROR:
sigmsg = "EXCEPTION_IN_PAGE_ERROR";
break;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
sigmsg = "EXCEPTION_INT_DIVIDE_BY_ZERO";
break;
case EXCEPTION_INT_OVERFLOW:
sigmsg = "EXCEPTION_INT_OVERFLOW";
break;
case EXCEPTION_INVALID_DISPOSITION:
sigmsg = "EXCEPTION_INVALID_DISPOSITION";
break;
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
sigmsg = "EXCEPTION_NONCONTINUABLE_EXCEPTION";
break;
case EXCEPTION_PRIV_INSTRUCTION:
sigmsg = "EXCEPTION_PRIV_INSTRUCTION";
break;
case EXCEPTION_SINGLE_STEP:
sigmsg = "EXCEPTION_SINGLE_STEP";
break;
case EXCEPTION_STACK_OVERFLOW:
sigmsg = "EXCEPTION_STACK_OVERFLOW";
break;
default:
sigmsg = "";
sprintf(msg, "unknown exception %d", num);
break;
}
#else
switch (num)
{
// case SIGINT:
@ -405,30 +488,132 @@ static void I_ReportSignal(int num, int coredumped)
else
sigmsg = msg;
}
#endif
if (sigmsg)
{
strcpy(msg, sigmsg);
}
if (coredumped)
{
if (sigmsg)
strcpy(msg, sigmsg);
strcat(msg, " (core dumped)");
sigmsg = msg;
}
#ifdef HAVE_CPPTRACE
strncat(msg, "\n", sizeof(msg) - strlen(msg) - 1);
cpptrace::stacktrace const& trace = *(cpptrace::stacktrace*)tracefromcpptrace;
bool firstfound = false;
#ifndef _WIN32
firstfound = true;
#endif
for (const auto& frame : trace)
{
#ifdef _WIN32
// dumb hack, unsure if it works on anything other than windows 10-11
if (!firstfound && frame.symbol == "KiUserExceptionDispatcher")
{
firstfound = true;
continue;
}
if (!firstfound)
{
continue;
}
#endif
srb2::String frame_str;
if (!frame.filename.empty() && frame.line.has_value())
{
frame_str = srb2::format("{} at {}:{}\n", frame.symbol, frame.filename, frame.line.value_or(0));
}
else if (!frame.filename.empty() && !frame.line.has_value())
{
frame_str = srb2::format("{} at {}\n", frame.symbol, frame.filename);
}
else
{
frame_str = srb2::format("{}\n", frame.symbol);
}
strncat(msg, frame_str.c_str(), sizeof(msg) - strlen(msg) - 1);
}
#endif
sigmsg = msg;
I_OutputMsg("\nProcess killed by signal: %s\n\n", sigmsg);
I_ShowErrorMessageBox(sigmsg,
#if defined (UNIXBACKTRACE)
true
#elif defined (_WIN32)
#elif defined (_WIN32) && defined (__GNUC__)
!M_CheckParm("-noexchndl")
#elif defined (USE_DBGHELP)
true
#else
false
#endif
);
}
#ifndef NEWSIGNALHANDLER
#if !defined(NEWSIGNALHANDLER) || defined(USE_DBGHELP)
static void CommonSignalHandleCleanup(void)
{
D_QuitNetGame(); // Fix server freezes
CL_AbortDownloadResume();
G_DirtyGameData();
}
#endif
#ifdef USE_DBGHELP
LPTOP_LEVEL_EXCEPTION_FILTER g_previous_toplevelexceptionfilter;
static LONG WriteMinidumpExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo)
{
#ifdef HAVE_CPPTRACE
// Fully aware this is completely signal unsafe. We don't ever try to recover from signals, so who cares.
// If it breaks it breaks. We're not mission critical software.
cpptrace::stacktrace trace = cpptrace::generate_trace(0, 30);
#else
int trace = 0;
#endif
MINIDUMP_EXCEPTION_INFORMATION mei {};
mei.ExceptionPointers = ExceptionInfo;
mei.ClientPointers = TRUE;
mei.ThreadId = GetCurrentThreadId();
HANDLE outfile;
char outfilename[1024];
GetModuleFileNameA(NULL, outfilename, sizeof(outfilename));
strncat(outfilename, ".dmp", sizeof(outfilename) - strlen(outfilename) - 1);
outfile = CreateFileA(outfilename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
if (outfile == NULL)
{
return EXCEPTION_CONTINUE_SEARCH;
}
BOOL result;
result = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), outfile, MiniDumpNormal, &mei, NULL, NULL);
if (result == FALSE)
{
CloseHandle(outfile);
DeleteFileA("ringracers_minidump.dmp");
goto exit;
}
CloseHandle(outfile);
exit:
CommonSignalHandleCleanup();
I_ReportSignal(ExceptionInfo->ExceptionRecord->ExceptionCode, 0, (void*)&trace);
return EXCEPTION_CONTINUE_SEARCH;
}
#endif
#if !defined(NEWSIGNALHANDLER) && !defined(USE_DBGHELP)
static ATTRNORETURN void signal_handler(INT32 num)
{
g_in_exiting_signal_handler = true;
@ -441,18 +626,20 @@ static ATTRNORETURN void signal_handler(INT32 num)
exit(-2);
}
D_QuitNetGame(); // Fix server freezes
CL_AbortDownloadResume();
G_DirtyGameData();
CommonSignalHandleCleanup();
#ifdef UNIXBACKTRACE
write_backtrace(num);
#endif
I_ReportSignal(num, 0);
I_ReportSignal(num, 0, NULL);
signal(num, SIG_DFL); //default signal action
raise(num);
}
#endif
#ifdef USE_DBGHELP
LPTOP_LEVEL_EXCEPTION_FILTER g_prevtoplevelexceptionfilter;
#endif
FUNCNORETURN static ATTRNORETURN void quit_handler(int num)
{
signal(num, SIG_DFL); //default signal action
@ -829,12 +1016,17 @@ static void I_RegisterSignals (void)
// If these defines don't exist,
// then compilation would have failed above us...
#ifndef NEWSIGNALHANDLER
#if !defined(NEWSIGNALHANDLER) && !defined(USE_DBGHELP)
signal(SIGILL , signal_handler);
signal(SIGSEGV , signal_handler);
signal(SIGABRT , signal_handler);
signal(SIGFPE , signal_handler);
#endif
#ifdef USE_DBGHELP
// Initialize Windows SDK-specific crashdump handler (DbgHelp)
g_previous_toplevelexceptionfilter = SetUnhandledExceptionFilter(WriteMinidumpExceptionFilter);
#endif
}
#ifdef NEWSIGNALHANDLER
@ -1598,9 +1790,9 @@ static void I_Fork(void)
{
signum = WTERMSIG (status);
#ifdef WCOREDUMP
I_ReportSignal(signum, WCOREDUMP (status));
I_ReportSignal(signum, WCOREDUMP (status), NULL);
#else
I_ReportSignal(signum, 0);
I_ReportSignal(signum, 0, NULL);
#endif
status = 128 + signum;
}
@ -1656,7 +1848,7 @@ INT32 I_StartupSystem(void)
//
// I_Quit
//
void I_Quit(void)
FUNCNORETURN void ATTRNORETURN I_Quit(void)
{
static SDL_bool quiting = SDL_FALSE;
@ -1732,7 +1924,7 @@ static boolean shutdowning = false;
extern "C" consvar_t cv_fuzz;
void I_Error(const char *error, ...)
FUNCIERROR void ATTRNORETURN I_Error(const char *error, ...)
{
va_list argptr;
char buffer[8192];

View file

@ -155,7 +155,7 @@ public:
float left_scale = std::cos(sep_pan);
float right_scale = std::sin(sep_pan);
buffer[i] = {buffer[i].amplitudes[0] * volume_ * left_scale, buffer[i].amplitudes[1] * volume_ * right_scale};
buffer[i] = {std::clamp(buffer[i].amplitudes[0] * volume_ * left_scale, -1.f, 1.f), std::clamp(buffer[i].amplitudes[1] * volume_ * right_scale, -1.f, 1.f)};
}
return buffer.size();
@ -1046,13 +1046,8 @@ UINT32 I_SoundInputDequeueSamples(void *data, UINT32 len)
{
return 0;
}
UINT32 avail = SDL_GetQueuedAudioSize(g_input_device_id);
if (avail == 0)
{
return 0;
}
UINT32 ret = SDL_DequeueAudio(g_input_device_id, data, std::min(len, avail));
UINT32 ret = SDL_DequeueAudio(g_input_device_id, data, len);
return ret;
}

View file

@ -1164,12 +1164,18 @@ sfxinfo_t S_sfx[NUMSFX] =
{"gate04", false, 32, 64, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"gate05", false, 32, 64, -1, NULL, 0, -1, -1, LUMPERROR, ""},
// Wavedash
{"waved1", false, 32, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"waved2", false, 32, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"waved3", false, 32, 64, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"waved4", false, 32, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"waved5", false, 32, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
// Sonic Boom & Subsonic
{"sonbo1", false, 32, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"sonbo2", false, 32, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"sonbo3", false, 32, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
// Passing sounds
{"pass01", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"pass02", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},

View file

@ -1239,6 +1239,11 @@ typedef enum
sfx_waved3,
sfx_waved4,
sfx_waved5,
// Sonic Boom & Subsonic
sfx_sonbo1,
sfx_sonbo2,
sfx_sonbo3,
// Passing sounds
sfx_pass01,

View file

@ -4,7 +4,7 @@
// it's only for detection of the version the player is using so the MS can alert them of an update.
// Only set it higher, not lower, obviously.
// Note that we use this to help keep internal testing in check; this is why v2.0 is not version "2".
#define MODVERSION 8
#define MODVERSION 9
// Define this as a prerelease version suffix
#define BETAVERSION "RC5"
#define BETAVERSION "RC6"

View file

@ -1397,7 +1397,7 @@ lumpnum_t W_CheckNumForName(const char *name)
lumpnumcacheindex = (lumpnumcacheindex + 1) & (LUMPNUMCACHESIZE - 1);
memset(lumpnumcache[lumpnumcacheindex].lumpname, '\0', LUMPNUMCACHENAME);
strncpy(lumpnumcache[lumpnumcacheindex].lumpname, name, 8);
lumpnumcache[lumpnumcacheindex].lumpnum = (i << 16) + check;
lumpnumcache[lumpnumcacheindex].lumpnum = (i << 16) | check;
lumpnumcache[lumpnumcacheindex].lumphash = hash;
return lumpnumcache[lumpnumcacheindex].lumpnum;
@ -1454,11 +1454,11 @@ lumpnum_t W_CheckNumForLongName(const char *name)
lumpnumcacheindex = (lumpnumcacheindex + 1) & (LUMPNUMCACHESIZE - 1);
memset(lumpnumcache[lumpnumcacheindex].lumpname, '\0', LUMPNUMCACHENAME);
strlcpy(lumpnumcache[lumpnumcacheindex].lumpname, name, LUMPNUMCACHENAME);
lumpnumcache[lumpnumcacheindex].lumpnum = (i << 16) + check;
lumpnumcache[lumpnumcacheindex].lumpnum = (i << 16) | check;
lumpnumcache[lumpnumcacheindex].lumphash = hash;
}
return (i << 16) + check;
return (i << 16) | check;
}
}
@ -1505,11 +1505,11 @@ lumpnum_t W_CheckNumForMap(const char *name, boolean checktofirst)
lumpnumcacheindex = (lumpnumcacheindex + 1) & (LUMPNUMCACHESIZE - 1);
memset(lumpnumcache[lumpnumcacheindex].lumpname, '\0', LUMPNUMCACHENAME);
strlcpy(lumpnumcache[lumpnumcacheindex].lumpname, name, LUMPNUMCACHENAME);
lumpnumcache[lumpnumcacheindex].lumpnum = (i << 16) + check;
lumpnumcache[lumpnumcacheindex].lumpnum = (i << 16) | check;
lumpnumcache[lumpnumcacheindex].lumphash = hash;
}
return (i << 16) + check;
return (i << 16) | check;
}
}
@ -1608,7 +1608,7 @@ lumpnum_t W_CheckNumForNameInFolder(const char *lump, const char *folder)
check = W_CheckNumForLongNamePwad(lump, (UINT16)i, fsid);
if (check < feid)
{
return (i<<16) + check; // found it, in our constraints
return (i<<16) | check; // found it, in our constraints
}
}
}
@ -1634,6 +1634,93 @@ UINT8 W_LumpExists(const char *name)
return false;
}
// Thanks to the introduction of "client side WAD files",
// a notion which is insanity in any other branch of DOOM,
// any direct wadnum ID is not a guaranteed index (and
// lumpnum_t, which has it in their upper bits, suffer too)
// We can do an O(n) conversion back and forth, which is
// better than nothing, but still kind of annoying to do.
// It was either this or killing musicwads lmao ~toast 180925
lumpnum_t W_LumpIntoNetSave(lumpnum_t lump)
{
UINT32 wad = (lump >> 16);
if (lump == LUMPERROR // Bad already
|| wad < mainwads) // Same between client/server
{
// Give what we get.
return lump;
}
if (wad >= numwadfiles // Outside of range
|| !wadfiles[wad]->important) // Can't convert local lumpnum
{
// No good return result!
return LUMPERROR;
}
// Count previous local files the client might not have.
UINT32 i = (mainwads + musicwads), localoffset = 0;
for (; i < wad; i++)
{
if (wadfiles[i]->important)
continue;
localoffset++;
}
if (!localoffset)
{
// No local files, return unchanged.
return lump;
}
if (localoffset <= wad)
{
// Success, return with the conversion.
return ((wad - localoffset) << 16) | (lump & UINT16_MAX);
}
// Death!!
return LUMPERROR;
}
lumpnum_t W_LumpFromNetSave(lumpnum_t lump)
{
UINT32 netwad = (lump >> 16);
if (lump == LUMPERROR // Bad already
|| netwad < mainwads) // Same between client/server
{
// Give what we get.
return lump;
}
// Count previous local files the server would ignore.
UINT32 i = (mainwads + musicwads), localoffset = 0;
for (; (i - localoffset) <= netwad && i < numwadfiles; i++)
{
if (wadfiles[i]->important)
continue;
localoffset++;
}
if (!localoffset)
{
// No local files, return unchanged.
return lump;
}
if (netwad + localoffset < numwadfiles)
{
// Success, return with the conversion.
return ((netwad + localoffset) << 16) | (lump & UINT16_MAX);
}
// Death!!
return LUMPERROR;
}
size_t W_LumpLengthPwad(UINT16 wad, UINT16 lump)
{
if (!TestValidLump(wad, lump))

View file

@ -188,6 +188,9 @@ lumpnum_t W_CheckNumForNameInBlock(const char *name, const char *blockstart, con
lumpnum_t W_CheckNumForNameInFolder(const char *lump, const char *folder);
UINT8 W_LumpExists(const char *name); // Lua uses this.
lumpnum_t W_LumpIntoNetSave(lumpnum_t lump);
lumpnum_t W_LumpFromNetSave(lumpnum_t lump);
size_t W_LumpLengthPwad(UINT16 wad, UINT16 lump);
size_t W_LumpLength(lumpnum_t lumpnum);

View file

@ -4,6 +4,10 @@
"version": "1.0.0",
"builtin-baseline": "d5ec528843d29e3a52d745a64b469f810b2cedbf",
"dependencies": [
{
"name": "cpptrace",
"platform": "windows & !mingw"
},
"curl",
"libogg",
"libpng",