Skip to content

Commit 89c234a

Browse files
committed
Multiplayer spectate mode, added OvertimeSecs and JoinCooldownSecs
1 parent 73bb2a5 commit 89c234a

File tree

16 files changed

+794
-73
lines changed

16 files changed

+794
-73
lines changed

Docs/Snippets/ServerConfiguration.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@
5252
"TotalKills": 10,
5353
"TotalLaps": 3,
5454
"TotalTreasureCollected": 0,
55+
"OvertimeSecs": 0,
56+
57+
/* Spectating */
58+
"EnableSpectate": true,
59+
"EnableFreeCamera": true,
60+
"AllowJoinDuringRound": true,
61+
"JoinCooldownSecs": 0,
5562

5663
/* Playlist can contain unlimited number of entries */
5764
"Playlist":
@@ -66,7 +73,8 @@
6673
"SpawnInvulnerableSecs": 4,
6774
"TotalKills": 10,
6875
"TotalLaps": 3,
69-
"TotalTreasureCollected": 0
76+
"TotalTreasureCollected": 0,
77+
"OvertimeSecs": 60
7078
},
7179
{
7280
"LevelName": "battle1",

Sources/Jazz2/Actors/Player.cpp

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ namespace Jazz2::Actors
129129
case PlayerType::Spaz: async_await RequestMetadataAsync("Interactive/PlayerSpaz"_s); break;
130130
case PlayerType::Lori: async_await RequestMetadataAsync("Interactive/PlayerLori"_s); break;
131131
case PlayerType::Frog: async_await RequestMetadataAsync("Interactive/PlayerFrog"_s); break;
132+
case PlayerType::Spectate:
133+
// TODO: Spectate mode - load minimal metadata for fallback, but player will be invisible
134+
async_await RequestMetadataAsync("Interactive/PlayerJazz"_s);
135+
break;
132136
}
133137

134138
SetAnimation(AnimState::Fall);
@@ -141,11 +145,25 @@ namespace Jazz2::Actors
141145
_weaponAmmo[(std::int32_t)WeaponType::Blaster] = UINT16_MAX;
142146
_weaponAmmoCheckpoint[(std::int32_t)WeaponType::Blaster] = UINT16_MAX;
143147

144-
SetState(ActorState::PreserveOnRollback | ActorState::CollideWithTilesetReduced | ActorState::CollideWithSolidObjects |
145-
ActorState::IsSolidObject | ActorState::ExcludeSimilar, true);
148+
if (_playerType == PlayerType::Spectate) {
149+
// Spectate mode - no collision, no gravity, invisible
150+
SetState(ActorState::PreserveOnRollback | ActorState::ExcludeSimilar, true);
151+
SetState(ActorState::CollideWithTilesetReduced | ActorState::CollideWithSolidObjects | ActorState::IsSolidObject, false);
152+
_health = 0;
153+
_maxHealth = 0;
154+
_renderer.setDrawEnabled(false);
155+
156+
// Empty hitbox for spectate mode
157+
AABB = {};
158+
AABBInner = {};
159+
} else {
160+
SetState(ActorState::PreserveOnRollback | ActorState::CollideWithTilesetReduced | ActorState::CollideWithSolidObjects |
161+
ActorState::IsSolidObject | ActorState::ExcludeSimilar, true);
162+
163+
_health = 5;
164+
_maxHealth = _health;
165+
}
146166

147-
_health = 5;
148-
_maxHealth = _health;
149167
_currentWeapon = WeaponType::Blaster;
150168

151169
_checkpointPos = Vector2f((float)details.Pos.X, (float)details.Pos.Y);
@@ -217,6 +235,11 @@ namespace Jazz2::Actors
217235
}
218236
#endif
219237

238+
if DEATH_UNLIKELY(_playerType == PlayerType::Spectate) {
239+
OnHandleSpectate(timeMult);
240+
return;
241+
}
242+
220243
// Delayed spawning
221244
if (_lastExitType != ExitType::None) {
222245
_controllableTimeout -= timeMult;
@@ -1483,6 +1506,11 @@ namespace Jazz2::Actors
14831506

14841507
void Player::OnUpdateHitbox()
14851508
{
1509+
if (_playerType == PlayerType::Spectate) {
1510+
AABBInner = {};
1511+
return;
1512+
}
1513+
14861514
// The sprite is always located relative to the hotspot.
14871515
// The coldspot is usually located at the ground level of the sprite,
14881516
// but for falling sprites for some reason somewhere above the hotspot instead.
@@ -2622,6 +2650,28 @@ namespace Jazz2::Actors
26222650
}
26232651
}
26242652

2653+
void Player::OnHandleSpectate(float timeMult)
2654+
{
2655+
constexpr float SpectateSpeed = 8.0f;
2656+
2657+
float playerMovement = _levelHandler->PlayerHorizontalMovement(this);
2658+
if (std::abs(playerMovement) > 0.5f) {
2659+
_speed.X = playerMovement * SpectateSpeed;
2660+
} else {
2661+
_speed.X = 0.0f;
2662+
}
2663+
2664+
float playerMovementVert = _levelHandler->PlayerVerticalMovement(this);
2665+
if (std::abs(playerMovementVert) > 0.5f) {
2666+
_speed.Y = playerMovementVert * SpectateSpeed;
2667+
} else {
2668+
_speed.Y = 0.0f;
2669+
}
2670+
2671+
_pos.X += _speed.X * timeMult;
2672+
_pos.Y += _speed.Y * timeMult;
2673+
}
2674+
26252675
std::shared_ptr<AudioBufferPlayer> Player::PlayPlayerSfx(StringView identifier, float gain, float pitch)
26262676
{
26272677
auto it = _metadata->Sounds.find(String::nullTerminatedView(identifier));

Sources/Jazz2/Actors/Player.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ namespace Jazz2::Actors
414414
void OnHandleMovement(float timeMult, bool areaWeaponAllowed, bool canJumpPrev);
415415
void OnHandleWater();
416416
void OnHandleAreaEvents(float timeMult, bool& areaWeaponAllowed, std::int32_t& areaWaterBlock);
417+
void OnHandleSpectate(float timeMult);
417418
void DoWarpOut(Vector2f pos, WarpFlags flags);
418419
void InitialPoleStage(bool horizontal);
419420
void NextPoleStage(bool horizontal, bool positive, std::int32_t stagesLeft, float lastSpeed);

Sources/Jazz2/LevelHandler.cpp

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1846,6 +1846,35 @@ namespace Jazz2
18461846
#endif
18471847
}
18481848

1849+
void LevelHandler::UnassignViewport(Actors::Player* player)
1850+
{
1851+
bool success = false;
1852+
for (std::size_t i = 0; i < _assignedViewports.size(); i++) {
1853+
if (_assignedViewports[i]->_targetActor == player) {
1854+
_assignedViewports.eraseUnordered(i);
1855+
success = true;
1856+
break;
1857+
}
1858+
}
1859+
1860+
#if defined(WITH_AUDIO)
1861+
if (success) {
1862+
for (auto& current : _playingSounds) {
1863+
if (auto* currentForSplitscreen = runtime_cast<AudioBufferPlayerForSplitscreen>(current.get())) {
1864+
currentForSplitscreen->updateViewports(_assignedViewports);
1865+
}
1866+
}
1867+
}
1868+
#endif
1869+
}
1870+
1871+
void LevelHandler::CommitViewports()
1872+
{
1873+
Viewport::GetChain().clear();
1874+
Vector2i res = theApplication().GetResolution();
1875+
OnInitializeViewport(res.X, res.Y);
1876+
}
1877+
18491878
void LevelHandler::InitializeCamera(Rendering::PlayerViewport& viewport)
18501879
{
18511880
if (viewport._targetActor == nullptr) {
@@ -2150,7 +2179,7 @@ namespace Jazz2
21502179
}
21512180

21522181
if (richPresence.LargeImage.empty()) {
2153-
richPresence.Details = "Playing as "_s;
2182+
richPresence.Details = "Playing"_s;
21542183
richPresence.LargeImage = "main-transparent"_s;
21552184

21562185
if (!_players.empty())
@@ -2161,15 +2190,15 @@ namespace Jazz2
21612190
case PlayerType::Lori: richPresence.SmallImage = "playing-lori"_s; break;
21622191
}
21632192
} else {
2164-
richPresence.Details = "Playing episode as "_s;
2193+
richPresence.Details = "Playing episode"_s;
21652194
}
21662195

21672196
if (!_players.empty()) {
21682197
switch (_players[0]->GetPlayerType()) {
21692198
default:
2170-
case PlayerType::Jazz: richPresence.Details += "Jazz"_s; break;
2171-
case PlayerType::Spaz: richPresence.Details += "Spaz"_s; break;
2172-
case PlayerType::Lori: richPresence.Details += "Lori"_s; break;
2199+
case PlayerType::Jazz: richPresence.Details += " as Jazz"_s; break;
2200+
case PlayerType::Spaz: richPresence.Details += " as Spaz"_s; break;
2201+
case PlayerType::Lori: richPresence.Details += " as Lori"_s; break;
21732202
}
21742203
}
21752204

Sources/Jazz2/LevelHandler.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,10 @@ namespace Jazz2
301301
void ResolveCollisions(float timeMult);
302302
/** @brief Assigns viewport */
303303
void AssignViewport(Actors::Player* player);
304+
/** @brief Unassigns viewport */
305+
void UnassignViewport(Actors::Player* player);
306+
/** @brief Commits changes in assigned viewports */
307+
void CommitViewports();
304308
/** @brief Initializes camera for specified viewport */
305309
void InitializeCamera(Rendering::PlayerViewport& viewport);
306310
/** @brief Updates pressed actions */

0 commit comments

Comments
 (0)