diff --git a/data/extras.png b/data/extras.png index b2c6deea1..14d320e1e 100644 Binary files a/data/extras.png and b/data/extras.png differ diff --git a/datasrc/content.py b/datasrc/content.py index 52353c491..7cb5fbe1a 100644 --- a/datasrc/content.py +++ b/datasrc/content.py @@ -452,6 +452,7 @@ container.sprites.Add(Sprite("hud_lock_mode", set_hud, 10,6,2,2)) container.sprites.Add(Sprite("hud_team0_mode", set_hud, 12,6,2,2)) container.sprites.Add(Sprite("part_snowflake", set_extras, 0,0,2,2)) +container.sprites.Add(Sprite("part_sparkle", set_extras, 2,0,2,2)) anim = Animation("base") diff --git a/datasrc/network.py b/datasrc/network.py index 04c02748b..01c6661e2 100644 --- a/datasrc/network.py +++ b/datasrc/network.py @@ -11,7 +11,7 @@ CharacterFlags = ["SOLO", "JETPACK", "COLLISION_DISABLED", "ENDLESS_HOOK", "ENDL "HAMMER_HIT_DISABLED", "SHOTGUN_HIT_DISABLED", "GRENADE_HIT_DISABLED", "LASER_HIT_DISABLED", "HOOK_HIT_DISABLED", "TELEGUN_GUN", "TELEGUN_GRENADE", "TELEGUN_LASER", "WEAPON_HAMMER", "WEAPON_GUN", "WEAPON_SHOTGUN", "WEAPON_GRENADE", "WEAPON_LASER", "WEAPON_NINJA", - "MOVEMENTS_DISABLED", "IN_FREEZE", "PRACTICE_MODE", "LOCK_MODE", "TEAM0_MODE"] + "MOVEMENTS_DISABLED", "IN_FREEZE", "PRACTICE_MODE", "LOCK_MODE", "TEAM0_MODE", "INVINCIBLE"] GameInfoFlags = [ "TIMESCORE", "GAMETYPE_RACE", "GAMETYPE_FASTCAP", "GAMETYPE_FNG", "GAMETYPE_DDRACE", "GAMETYPE_DDNET", "GAMETYPE_BLOCK_WORLDS", diff --git a/src/game/client/components/effects.cpp b/src/game/client/components/effects.cpp index d5c7fd608..ae2a1bdb4 100644 --- a/src/game/client/components/effects.cpp +++ b/src/game/client/components/effects.cpp @@ -103,6 +103,26 @@ void CEffects::FreezingFlakes(vec2 Pos, vec2 Size, float Alpha) m_pClient->m_Particles.Add(CParticles::GROUP_EXTRA, &p); } +void CEffects::SparkleTrail(vec2 Pos, float Alpha) +{ + if(!m_Add50hz) + return; + + CParticle p; + p.SetDefault(); + p.m_Spr = SPRITE_PART_SPARKLE; + p.m_Pos = Pos + random_direction() * random_float(40.0f); + p.m_Vel = vec2(0, 0); + p.m_LifeSpan = 0.5f; + p.m_StartSize = 0.0f; + p.m_EndSize = random_float(20.0f, 30.0f); + p.m_UseAlphaFading = true; + p.m_StartAlpha = Alpha; + p.m_EndAlpha = std::min(0.2f, Alpha); + p.m_Collides = false; + m_pClient->m_Particles.Add(CParticles::GROUP_TRAIL_EXTRA, &p); +} + void CEffects::SmokeTrail(vec2 Pos, vec2 Vel, float Alpha, float TimePassed) { if(!m_Add50hz && TimePassed < 0.001f) diff --git a/src/game/client/components/effects.h b/src/game/client/components/effects.h index de75e63c9..703eff397 100644 --- a/src/game/client/components/effects.h +++ b/src/game/client/components/effects.h @@ -30,6 +30,7 @@ public: void PlayerDeath(vec2 Pos, int ClientId, float Alpha = 1.0f); void PowerupShine(vec2 Pos, vec2 Size, float Alpha = 1.0f); void FreezingFlakes(vec2 Pos, vec2 Size, float Alpha = 1.0f); + void SparkleTrail(vec2 Pos, float Alpha = 1.0f); void Confetti(vec2 Pos, float Alpha = 1.0f); void Update(); diff --git a/src/game/client/components/particles.cpp b/src/game/client/components/particles.cpp index fef6e886d..21ef0ce35 100644 --- a/src/game/client/components/particles.cpp +++ b/src/game/client/components/particles.cpp @@ -14,6 +14,7 @@ CParticles::CParticles() { OnReset(); m_RenderTrail.m_pParts = this; + m_RenderTrailExtra.m_pParts = this; m_RenderExplosions.m_pParts = this; m_RenderExtra.m_pParts = this; m_RenderGeneral.m_pParts = this; @@ -181,9 +182,12 @@ void CParticles::OnInit() m_ExtraParticleQuadContainerIndex = Graphics()->CreateQuadContainer(false); - // TODO: Use a loop similar to the one for m_ParticleQuadContainerIndex if you add more additional particles - Graphics()->QuadsSetSubset(0, 0, 1, 1); - RenderTools()->QuadContainerAddSprite(m_ExtraParticleQuadContainerIndex, 1.f); + for(int i = 0; i <= (SPRITE_PART_SPARKLE - SPRITE_PART_SNOWFLAKE); ++i) + { + Graphics()->QuadsSetSubset(0, 0, 1, 1); + RenderTools()->QuadContainerAddSprite(m_ExtraParticleQuadContainerIndex, 1.f); + } + Graphics()->QuadContainerUpload(m_ExtraParticleQuadContainerIndex); } @@ -207,7 +211,7 @@ void CParticles::RenderGroup(int Group) IGraphics::CTextureHandle *aParticles = GameClient()->m_ParticlesSkin.m_aSpriteParticles; int FirstParticleOffset = SPRITE_PART_SLICE; int ParticleQuadContainerIndex = m_ParticleQuadContainerIndex; - if(Group == GROUP_EXTRA) + if(Group == GROUP_EXTRA || Group == GROUP_TRAIL_EXTRA) { aParticles = GameClient()->m_ExtrasSkin.m_aSpriteParticles; FirstParticleOffset = SPRITE_PART_SNOWFLAKE; diff --git a/src/game/client/components/particles.h b/src/game/client/components/particles.h index 0d2f4937d..16c06b5d9 100644 --- a/src/game/client/components/particles.h +++ b/src/game/client/components/particles.h @@ -68,6 +68,7 @@ public: enum { GROUP_PROJECTILE_TRAIL = 0, + GROUP_TRAIL_EXTRA, GROUP_EXPLOSIONS, GROUP_EXTRA, GROUP_GENERAL, @@ -111,7 +112,10 @@ private: virtual void OnRender() override { m_pParts->RenderGroup(TGROUP); } }; + // behind players CRenderGroup m_RenderTrail; + CRenderGroup m_RenderTrailExtra; + // in front of players CRenderGroup m_RenderExplosions; CRenderGroup m_RenderExtra; CRenderGroup m_RenderGeneral; diff --git a/src/game/client/components/players.cpp b/src/game/client/components/players.cpp index 3876c5331..368916695 100644 --- a/src/game/client/components/players.cpp +++ b/src/game/client/components/players.cpp @@ -742,6 +742,10 @@ void CPlayers::RenderPlayer( { GameClient()->m_Effects.FreezingFlakes(BodyPos, vec2(32, 32), Alpha); } + if(RenderInfo.m_TeeRenderFlags & TEE_EFFECT_SPARKLE) + { + GameClient()->m_Effects.SparkleTrail(BodyPos, Alpha); + } if(ClientId < 0) return; @@ -833,6 +837,8 @@ void CPlayers::OnRender() aRenderInfo[i].m_TeeRenderFlags |= TEE_EFFECT_FROZEN | TEE_NO_WEAPON; if(m_pClient->m_aClients[i].m_LiveFrozen) aRenderInfo[i].m_TeeRenderFlags |= TEE_EFFECT_FROZEN; + if(m_pClient->m_aClients[i].m_Invincible) + aRenderInfo[i].m_TeeRenderFlags |= TEE_EFFECT_SPARKLE; const CGameClient::CSnapState::CCharacterInfo &CharacterInfo = m_pClient->m_Snap.m_aCharacters[i]; const bool Frozen = CharacterInfo.m_HasExtendedData && CharacterInfo.m_ExtendedData.m_FreezeEnd != 0; diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index cc5fc66b3..9e5011595 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -128,6 +128,7 @@ void CGameClient::OnConsoleInit() &m_Background, // render instead of m_MapLayersBackground when g_Config.m_ClOverlayEntities == 100 &m_MapLayersBackground, // first to render &m_Particles.m_RenderTrail, + &m_Particles.m_RenderTrailExtra, &m_Items, &m_Ghost, &m_Players, @@ -1683,6 +1684,7 @@ void CGameClient::OnNewSnapshot() pClient->m_ShotgunHitDisabled = pCharacterData->m_Flags & CHARACTERFLAG_SHOTGUN_HIT_DISABLED; pClient->m_HookHitDisabled = pCharacterData->m_Flags & CHARACTERFLAG_HOOK_HIT_DISABLED; pClient->m_Super = pCharacterData->m_Flags & CHARACTERFLAG_SUPER; + pClient->m_Invincible = pCharacterData->m_Flags & CHARACTERFLAG_INVINCIBLE; // Endless pClient->m_EndlessHook = pCharacterData->m_Flags & CHARACTERFLAG_ENDLESS_HOOK; @@ -2457,6 +2459,7 @@ void CGameClient::CClientData::Reset() m_ShotgunHitDisabled = false; m_HookHitDisabled = false; m_Super = false; + m_Invincible = false; m_HasTelegunGun = false; m_HasTelegunGrenade = false; m_HasTelegunLaser = false; @@ -3694,6 +3697,7 @@ void CGameClient::LoadExtrasSkin(const char *pPath, bool AsDir) if(m_ExtrasSkinLoaded) { Graphics()->UnloadTexture(&m_ExtrasSkin.m_SpriteParticleSnowflake); + Graphics()->UnloadTexture(&m_ExtrasSkin.m_SpriteParticleSparkle); for(auto &SpriteParticle : m_ExtrasSkin.m_aSpriteParticles) SpriteParticle = IGraphics::CTextureHandle(); @@ -3728,7 +3732,11 @@ void CGameClient::LoadExtrasSkin(const char *pPath, bool AsDir) else if(PngLoaded && Graphics()->CheckImageDivisibility(aPath, ImgInfo, g_pData->m_aSprites[SPRITE_PART_SNOWFLAKE].m_pSet->m_Gridx, g_pData->m_aSprites[SPRITE_PART_SNOWFLAKE].m_pSet->m_Gridy, true) && Graphics()->IsImageFormatRgba(aPath, ImgInfo)) { m_ExtrasSkin.m_SpriteParticleSnowflake = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_PART_SNOWFLAKE]); + m_ExtrasSkin.m_SpriteParticleSparkle = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_PART_SPARKLE]); + m_ExtrasSkin.m_aSpriteParticles[0] = m_ExtrasSkin.m_SpriteParticleSnowflake; + m_ExtrasSkin.m_aSpriteParticles[1] = m_ExtrasSkin.m_SpriteParticleSparkle; + m_ExtrasSkinLoaded = true; } ImgInfo.Free(); diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h index 11fd32f27..8e911f51b 100644 --- a/src/game/client/gameclient.h +++ b/src/game/client/gameclient.h @@ -394,6 +394,7 @@ public: bool m_ShotgunHitDisabled; bool m_HookHitDisabled; bool m_Super; + bool m_Invincible; bool m_HasTelegunGun; bool m_HasTelegunGrenade; bool m_HasTelegunLaser; @@ -768,7 +769,8 @@ public: struct SClientExtrasSkin { IGraphics::CTextureHandle m_SpriteParticleSnowflake; - IGraphics::CTextureHandle m_aSpriteParticles[1]; + IGraphics::CTextureHandle m_SpriteParticleSparkle; + IGraphics::CTextureHandle m_aSpriteParticles[2]; }; SClientExtrasSkin m_ExtrasSkin; diff --git a/src/game/client/prediction/entities/character.cpp b/src/game/client/prediction/entities/character.cpp index 8ce77b67e..a3369651b 100644 --- a/src/game/client/prediction/entities/character.cpp +++ b/src/game/client/prediction/entities/character.cpp @@ -757,19 +757,19 @@ void CCharacter::HandleTiles(int Index) Switchers()[Collision()->GetSwitchNumber(MapIndex)].m_aType[Team()] = TILE_SWITCHCLOSE; Switchers()[Collision()->GetSwitchNumber(MapIndex)].m_aLastUpdateTick[Team()] = GameWorld()->GameTick(); } - else if(Collision()->GetSwitchType(MapIndex) == TILE_FREEZE && Team() != TEAM_SUPER) + else if(Collision()->GetSwitchType(MapIndex) == TILE_FREEZE && Team() != TEAM_SUPER && !m_Core.m_Invincible) { if(Collision()->GetSwitchNumber(MapIndex) == 0 || Switchers()[Collision()->GetSwitchNumber(MapIndex)].m_aStatus[Team()]) { Freeze(Collision()->GetSwitchDelay(MapIndex)); } } - else if(Collision()->GetSwitchType(MapIndex) == TILE_DFREEZE && Team() != TEAM_SUPER) + else if(Collision()->GetSwitchType(MapIndex) == TILE_DFREEZE && Team() != TEAM_SUPER && !m_Core.m_Invincible) { if(Collision()->GetSwitchNumber(MapIndex) == 0 || Switchers()[Collision()->GetSwitchNumber(MapIndex)].m_aStatus[Team()]) m_Core.m_DeepFrozen = true; } - else if(Collision()->GetSwitchType(MapIndex) == TILE_DUNFREEZE && Team() != TEAM_SUPER) + else if(Collision()->GetSwitchType(MapIndex) == TILE_DUNFREEZE && Team() != TEAM_SUPER && !m_Core.m_Invincible) { if(Collision()->GetSwitchNumber(MapIndex) == 0 || Switchers()[Collision()->GetSwitchNumber(MapIndex)].m_aStatus[Team()]) m_Core.m_DeepFrozen = false; @@ -817,14 +817,14 @@ void CCharacter::HandleTiles(int Index) if(NewJumps != m_Core.m_Jumps) m_Core.m_Jumps = NewJumps; } - else if(Collision()->GetSwitchType(MapIndex) == TILE_LFREEZE && Team() != TEAM_SUPER) + else if(Collision()->GetSwitchType(MapIndex) == TILE_LFREEZE && Team() != TEAM_SUPER && !m_Core.m_Invincible) { if(Collision()->GetSwitchNumber(MapIndex) == 0 || Switchers()[Collision()->GetSwitchNumber(MapIndex)].m_aStatus[Team()]) { m_Core.m_LiveFrozen = true; } } - else if(Collision()->GetSwitchType(MapIndex) == TILE_LUNFREEZE && Team() != TEAM_SUPER) + else if(Collision()->GetSwitchType(MapIndex) == TILE_LUNFREEZE && Team() != TEAM_SUPER && !m_Core.m_Invincible) { if(Collision()->GetSwitchNumber(MapIndex) == 0 || Switchers()[Collision()->GetSwitchNumber(MapIndex)].m_aStatus[Team()]) { @@ -833,7 +833,7 @@ void CCharacter::HandleTiles(int Index) } // freeze - if(((m_TileIndex == TILE_FREEZE) || (m_TileFIndex == TILE_FREEZE)) && !m_Core.m_Super && !m_Core.m_DeepFrozen) + if(((m_TileIndex == TILE_FREEZE) || (m_TileFIndex == TILE_FREEZE)) && !m_Core.m_Super && !m_Core.m_Invincible && !m_Core.m_DeepFrozen) { Freeze(); } @@ -843,21 +843,21 @@ void CCharacter::HandleTiles(int Index) } // deep freeze - if(((m_TileIndex == TILE_DFREEZE) || (m_TileFIndex == TILE_DFREEZE)) && !m_Core.m_Super && !m_Core.m_DeepFrozen) + if(((m_TileIndex == TILE_DFREEZE) || (m_TileFIndex == TILE_DFREEZE)) && !m_Core.m_Super && !m_Core.m_Invincible && !m_Core.m_DeepFrozen) { m_Core.m_DeepFrozen = true; } - else if(((m_TileIndex == TILE_DUNFREEZE) || (m_TileFIndex == TILE_DUNFREEZE)) && !m_Core.m_Super && m_Core.m_DeepFrozen) + else if(((m_TileIndex == TILE_DUNFREEZE) || (m_TileFIndex == TILE_DUNFREEZE)) && !m_Core.m_Super && !m_Core.m_Invincible && m_Core.m_DeepFrozen) { m_Core.m_DeepFrozen = false; } // live freeze - if(((m_TileIndex == TILE_LFREEZE) || (m_TileFIndex == TILE_LFREEZE)) && !m_Core.m_Super) + if(((m_TileIndex == TILE_LFREEZE) || (m_TileFIndex == TILE_LFREEZE)) && !m_Core.m_Super && !m_Core.m_Invincible) { m_Core.m_LiveFrozen = true; } - else if(((m_TileIndex == TILE_LUNFREEZE) || (m_TileFIndex == TILE_LUNFREEZE)) && !m_Core.m_Super) + else if(((m_TileIndex == TILE_LUNFREEZE) || (m_TileFIndex == TILE_LUNFREEZE)) && !m_Core.m_Super && !m_Core.m_Invincible) { m_Core.m_LiveFrozen = false; } @@ -959,7 +959,7 @@ void CCharacter::HandleTuneLayer() void CCharacter::DDRaceTick() { mem_copy(&m_Input, &m_SavedInput, sizeof(m_Input)); - if(m_Core.m_LiveFrozen && !m_CanMoveInFreeze && !m_Core.m_Super) + if(m_Core.m_LiveFrozen && !m_CanMoveInFreeze && !m_Core.m_Super && !m_Core.m_Invincible) { m_Input.m_Direction = 0; m_Input.m_Jump = 0; @@ -1007,7 +1007,7 @@ void CCharacter::DDRacePostCoreTick() m_FrozenLastTick = false; - if(m_Core.m_DeepFrozen && !m_Core.m_Super) + if(m_Core.m_DeepFrozen && !m_Core.m_Super && !m_Core.m_Invincible) Freeze(); // following jump rules can be overridden by tiles, like Refill Jumps, Stopper and Wall Jump @@ -1032,9 +1032,9 @@ void CCharacter::DDRacePostCoreTick() m_Core.m_Jumped = 1; } - if((m_Core.m_Super || m_Core.m_EndlessJump) && m_Core.m_Jumped > 1) + if((m_Core.m_Super || m_Core.m_Invincible || m_Core.m_EndlessJump) && m_Core.m_Jumped > 1) { - // Super players and players with infinite jumps always have light feet + // Super players, invincible players and players with infinite jumps always have light feet m_Core.m_Jumped = 1; } @@ -1056,7 +1056,7 @@ bool CCharacter::Freeze(int Seconds) { if(!GameWorld()->m_WorldConfig.m_PredictFreeze) return false; - if(Seconds <= 0 || m_Core.m_Super || m_FreezeTime > Seconds * GameWorld()->GameTickSpeed()) + if(Seconds <= 0 || m_Core.m_Super || m_Core.m_Invincible || m_FreezeTime > Seconds * GameWorld()->GameTickSpeed()) return false; if(m_Core.m_FreezeStart < GameWorld()->GameTick() - GameWorld()->GameTickSpeed()) { diff --git a/src/game/client/render.h b/src/game/client/render.h index 17d8ccfa7..11b7255b9 100644 --- a/src/game/client/render.h +++ b/src/game/client/render.h @@ -123,6 +123,7 @@ enum { TEE_EFFECT_FROZEN = 1, TEE_NO_WEAPON = 2, + TEE_EFFECT_SPARKLE = 4, }; // sprite renderings diff --git a/src/game/gamecore.cpp b/src/game/gamecore.cpp index 042e64579..45081cad3 100644 --- a/src/game/gamecore.cpp +++ b/src/game/gamecore.cpp @@ -173,6 +173,7 @@ void CCharacterCore::Reset() m_ShotgunHitDisabled = false; m_HookHitDisabled = false; m_Super = false; + m_Invincible = false; m_HasTelegunGun = false; m_HasTelegunGrenade = false; m_HasTelegunLaser = false; @@ -647,6 +648,7 @@ void CCharacterCore::ReadDDNet(const CNetObj_DDNetCharacter *pObjDDNet) m_LaserHitDisabled = pObjDDNet->m_Flags & CHARACTERFLAG_LASER_HIT_DISABLED; m_HookHitDisabled = pObjDDNet->m_Flags & CHARACTERFLAG_HOOK_HIT_DISABLED; m_Super = pObjDDNet->m_Flags & CHARACTERFLAG_SUPER; + m_Invincible = pObjDDNet->m_Flags & CHARACTERFLAG_INVINCIBLE; // Endless m_EndlessHook = pObjDDNet->m_Flags & CHARACTERFLAG_ENDLESS_HOOK; diff --git a/src/game/gamecore.h b/src/game/gamecore.h index c33b14e17..0cce06c2c 100644 --- a/src/game/gamecore.h +++ b/src/game/gamecore.h @@ -256,6 +256,7 @@ public: bool m_ShotgunHitDisabled; bool m_HookHitDisabled; bool m_Super; + bool m_Invincible; bool m_HasTelegunGun; bool m_HasTelegunGrenade; bool m_HasTelegunLaser; diff --git a/src/game/server/ddracechat.cpp b/src/game/server/ddracechat.cpp index 2e027aa4c..4a927d757 100644 --- a/src/game/server/ddracechat.cpp +++ b/src/game/server/ddracechat.cpp @@ -2169,6 +2169,13 @@ void CGameContext::ConPracticeUnNinja(IConsole::IResult *pResult, void *pUserDat ConUnNinja(pResult, pUserData); } +void CGameContext::ConPracticeToggleInvincible(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + if(pSelf->GetPracticeCharacter(pResult)) + ConToggleInvincible(pResult, pUserData); +} + void CGameContext::ConPracticeAddWeapon(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; diff --git a/src/game/server/ddracecommands.cpp b/src/game/server/ddracecommands.cpp index e65cad482..e7e9b8251 100644 --- a/src/game/server/ddracecommands.cpp +++ b/src/game/server/ddracecommands.cpp @@ -160,6 +160,14 @@ void CGameContext::ConUnSuper(IConsole::IResult *pResult, void *pUserData) } } +void CGameContext::ConToggleInvincible(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + CCharacter *pChr = pSelf->GetPlayerChar(pResult->m_ClientId); + if(pChr) + pChr->SetInvincible(pResult->NumArguments() == 0 ? !pChr->Core()->m_Invincible : pResult->GetInteger(0)); +} + void CGameContext::ConSolo(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index 44363e915..fe961f1e1 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -174,19 +174,35 @@ void CCharacter::SetSolo(bool Solo) void CCharacter::SetSuper(bool Super) { - m_Core.m_Super = Super; + // Disable invincible mode before activating super mode. Both modes active at the same time wouldn't necessarily break anything but it's not useful. if(Super) + SetInvincible(false); + + bool WasSuper = m_Core.m_Super; + m_Core.m_Super = Super; + if(Super && !WasSuper) { m_TeamBeforeSuper = Team(); Teams()->SetCharacterTeam(GetPlayer()->GetCid(), TEAM_SUPER); m_DDRaceState = DDRACE_CHEAT; } - else + else if(!Super && WasSuper) { Teams()->SetForceCharacterTeam(GetPlayer()->GetCid(), m_TeamBeforeSuper); } } +void CCharacter::SetInvincible(bool Invincible) +{ + // Disable super mode before activating invincible mode. Both modes active at the same time wouldn't necessarily break anything but it's not useful. + if(Invincible) + SetSuper(false); + + m_Core.m_Invincible = Invincible; + if(Invincible) + UnFreeze(); +} + void CCharacter::SetLiveFrozen(bool Active) { m_Core.m_LiveFrozen = Active; @@ -1219,6 +1235,8 @@ void CCharacter::Snap(int SnappingClient) pDDNetCharacter->m_Flags |= CHARACTERFLAG_SOLO; if(m_Core.m_Super) pDDNetCharacter->m_Flags |= CHARACTERFLAG_SUPER; + if(m_Core.m_Invincible) + pDDNetCharacter->m_Flags |= CHARACTERFLAG_INVINCIBLE; if(m_Core.m_EndlessHook) pDDNetCharacter->m_Flags |= CHARACTERFLAG_ENDLESS_HOOK; if(m_Core.m_CollisionDisabled || !Tuning()->m_PlayerCollision) @@ -1361,7 +1379,7 @@ void CCharacter::HandleSkippableTiles(int Index) Collision()->GetFCollisionAt(m_Pos.x + GetProximityRadius() / 3.f, m_Pos.y + GetProximityRadius() / 3.f) == TILE_DEATH || Collision()->GetFCollisionAt(m_Pos.x - GetProximityRadius() / 3.f, m_Pos.y - GetProximityRadius() / 3.f) == TILE_DEATH || Collision()->GetFCollisionAt(m_Pos.x - GetProximityRadius() / 3.f, m_Pos.y + GetProximityRadius() / 3.f) == TILE_DEATH) && - !m_Core.m_Super && !(Team() && Teams()->TeeFinished(m_pPlayer->GetCid()))) + !m_Core.m_Super && !m_Core.m_Invincible && !(Team() && Teams()->TeeFinished(m_pPlayer->GetCid()))) { Die(m_pPlayer->GetCid(), WEAPON_WORLD); return; @@ -1491,7 +1509,7 @@ void CCharacter::HandleTiles(int Index) return; // freeze - if(((m_TileIndex == TILE_FREEZE) || (m_TileFIndex == TILE_FREEZE)) && !m_Core.m_Super && !m_Core.m_DeepFrozen) + if(((m_TileIndex == TILE_FREEZE) || (m_TileFIndex == TILE_FREEZE)) && !m_Core.m_Super && !m_Core.m_Invincible && !m_Core.m_DeepFrozen) { Freeze(); } @@ -1499,17 +1517,17 @@ void CCharacter::HandleTiles(int Index) UnFreeze(); // deep freeze - if(((m_TileIndex == TILE_DFREEZE) || (m_TileFIndex == TILE_DFREEZE)) && !m_Core.m_Super && !m_Core.m_DeepFrozen) + if(((m_TileIndex == TILE_DFREEZE) || (m_TileFIndex == TILE_DFREEZE)) && !m_Core.m_Super && !m_Core.m_Invincible && !m_Core.m_DeepFrozen) m_Core.m_DeepFrozen = true; - else if(((m_TileIndex == TILE_DUNFREEZE) || (m_TileFIndex == TILE_DUNFREEZE)) && !m_Core.m_Super && m_Core.m_DeepFrozen) + else if(((m_TileIndex == TILE_DUNFREEZE) || (m_TileFIndex == TILE_DUNFREEZE)) && !m_Core.m_Super && !m_Core.m_Invincible && m_Core.m_DeepFrozen) m_Core.m_DeepFrozen = false; // live freeze - if(((m_TileIndex == TILE_LFREEZE) || (m_TileFIndex == TILE_LFREEZE)) && !m_Core.m_Super) + if(((m_TileIndex == TILE_LFREEZE) || (m_TileFIndex == TILE_LFREEZE)) && !m_Core.m_Super && !m_Core.m_Invincible) { m_Core.m_LiveFrozen = true; } - else if(((m_TileIndex == TILE_LUNFREEZE) || (m_TileFIndex == TILE_LUNFREEZE)) && !m_Core.m_Super) + else if(((m_TileIndex == TILE_LUNFREEZE) || (m_TileFIndex == TILE_LUNFREEZE)) && !m_Core.m_Super && !m_Core.m_Invincible) { m_Core.m_LiveFrozen = false; } @@ -1690,31 +1708,31 @@ void CCharacter::HandleTiles(int Index) Switchers()[Collision()->GetSwitchNumber(MapIndex)].m_aType[Team()] = TILE_SWITCHCLOSE; Switchers()[Collision()->GetSwitchNumber(MapIndex)].m_aLastUpdateTick[Team()] = Server()->Tick(); } - else if(Collision()->GetSwitchType(MapIndex) == TILE_FREEZE && Team() != TEAM_SUPER) + else if(Collision()->GetSwitchType(MapIndex) == TILE_FREEZE && Team() != TEAM_SUPER && !m_Core.m_Invincible) { if(Collision()->GetSwitchNumber(MapIndex) == 0 || Switchers()[Collision()->GetSwitchNumber(MapIndex)].m_aStatus[Team()]) { Freeze(Collision()->GetSwitchDelay(MapIndex)); } } - else if(Collision()->GetSwitchType(MapIndex) == TILE_DFREEZE && Team() != TEAM_SUPER) + else if(Collision()->GetSwitchType(MapIndex) == TILE_DFREEZE && Team() != TEAM_SUPER && !m_Core.m_Invincible) { if(Collision()->GetSwitchNumber(MapIndex) == 0 || Switchers()[Collision()->GetSwitchNumber(MapIndex)].m_aStatus[Team()]) m_Core.m_DeepFrozen = true; } - else if(Collision()->GetSwitchType(MapIndex) == TILE_DUNFREEZE && Team() != TEAM_SUPER) + else if(Collision()->GetSwitchType(MapIndex) == TILE_DUNFREEZE && Team() != TEAM_SUPER && !m_Core.m_Invincible) { if(Collision()->GetSwitchNumber(MapIndex) == 0 || Switchers()[Collision()->GetSwitchNumber(MapIndex)].m_aStatus[Team()]) m_Core.m_DeepFrozen = false; } - else if(Collision()->GetSwitchType(MapIndex) == TILE_LFREEZE && Team() != TEAM_SUPER) + else if(Collision()->GetSwitchType(MapIndex) == TILE_LFREEZE && Team() != TEAM_SUPER && !m_Core.m_Invincible) { if(Collision()->GetSwitchNumber(MapIndex) == 0 || Switchers()[Collision()->GetSwitchNumber(MapIndex)].m_aStatus[Team()]) { m_Core.m_LiveFrozen = true; } } - else if(Collision()->GetSwitchType(MapIndex) == TILE_LUNFREEZE && Team() != TEAM_SUPER) + else if(Collision()->GetSwitchType(MapIndex) == TILE_LUNFREEZE && Team() != TEAM_SUPER && !m_Core.m_Invincible) { if(Collision()->GetSwitchNumber(MapIndex) == 0 || Switchers()[Collision()->GetSwitchNumber(MapIndex)].m_aStatus[Team()]) { @@ -1846,7 +1864,7 @@ void CCharacter::HandleTiles(int Index) int z = Collision()->IsTeleport(MapIndex); if(!g_Config.m_SvOldTeleportHook && !g_Config.m_SvOldTeleportWeapons && z && !Collision()->TeleOuts(z - 1).empty()) { - if(m_Core.m_Super) + if(m_Core.m_Super || m_Core.m_Invincible) return; int TeleOut = GameWorld()->m_Core.RandomOr0(Collision()->TeleOuts(z - 1).size()); m_Core.m_Pos = Collision()->TeleOuts(z - 1)[TeleOut]; @@ -1861,7 +1879,7 @@ void CCharacter::HandleTiles(int Index) int evilz = Collision()->IsEvilTeleport(MapIndex); if(evilz && !Collision()->TeleOuts(evilz - 1).empty()) { - if(m_Core.m_Super) + if(m_Core.m_Super || m_Core.m_Invincible) return; int TeleOut = GameWorld()->m_Core.RandomOr0(Collision()->TeleOuts(evilz - 1).size()); m_Core.m_Pos = Collision()->TeleOuts(evilz - 1)[TeleOut]; @@ -1883,7 +1901,7 @@ void CCharacter::HandleTiles(int Index) } if(Collision()->IsCheckEvilTeleport(MapIndex)) { - if(m_Core.m_Super) + if(m_Core.m_Super || m_Core.m_Invincible) return; // first check if there is a TeleCheckOut for the current recorded checkpoint, if not check previous checkpoints for(int k = m_TeleCheckpoint - 1; k >= 0; k--) @@ -1920,7 +1938,7 @@ void CCharacter::HandleTiles(int Index) } if(Collision()->IsCheckTeleport(MapIndex)) { - if(m_Core.m_Super) + if(m_Core.m_Super || m_Core.m_Invincible) return; // first check if there is a TeleCheckOut for the current recorded checkpoint, if not check previous checkpoints for(int k = m_TeleCheckpoint - 1; k >= 0; k--) @@ -2066,7 +2084,7 @@ void CCharacter::DDRaceTick() if(m_Input.m_Direction != 0 || m_Input.m_Jump != 0) m_LastMove = Server()->Tick(); - if(m_Core.m_LiveFrozen && !m_Core.m_Super) + if(m_Core.m_LiveFrozen && !m_Core.m_Super && !m_Core.m_Invincible) { m_Input.m_Direction = 0; m_Input.m_Jump = 0; @@ -2120,7 +2138,7 @@ void CCharacter::DDRacePostCoreTick() m_FrozenLastTick = false; - if(m_Core.m_DeepFrozen && !m_Core.m_Super) + if(m_Core.m_DeepFrozen && !m_Core.m_Super && !m_Core.m_Invincible) Freeze(); // following jump rules can be overridden by tiles, like Refill Jumps, Stopper and Wall Jump @@ -2145,9 +2163,9 @@ void CCharacter::DDRacePostCoreTick() m_Core.m_Jumped = 1; } - if((m_Core.m_Super || m_Core.m_EndlessJump) && m_Core.m_Jumped > 1) + if((m_Core.m_Super || m_Core.m_Invincible || m_Core.m_EndlessJump) && m_Core.m_Jumped > 1) { - // Super players and players with infinite jumps always have light feet + // Super players, invincible players and players with infinite jumps always have light feet m_Core.m_Jumped = 1; } @@ -2192,7 +2210,7 @@ void CCharacter::DDRacePostCoreTick() bool CCharacter::Freeze(int Seconds) { - if(Seconds <= 0 || m_Core.m_Super || m_FreezeTime > Seconds * Server()->TickSpeed()) + if(Seconds <= 0 || m_Core.m_Super || m_Core.m_Invincible || m_FreezeTime > Seconds * Server()->TickSpeed()) return false; if(m_FreezeTime == 0 || m_Core.m_FreezeStart < Server()->Tick() - Server()->TickSpeed()) { @@ -2366,7 +2384,7 @@ void CCharacter::DDRaceInit() void CCharacter::Rescue() { - if(m_SetSavePos[GetPlayer()->m_RescueMode] && !m_Core.m_Super) + if(m_SetSavePos[GetPlayer()->m_RescueMode] && !m_Core.m_Super && !m_Core.m_Invincible) { if(m_LastRescue + (int64_t)g_Config.m_SvRescueDelay * Server()->TickSpeed() > Server()->Tick()) { diff --git a/src/game/server/entities/character.h b/src/game/server/entities/character.h index df569dc2c..59793707c 100644 --- a/src/game/server/entities/character.h +++ b/src/game/server/entities/character.h @@ -51,6 +51,7 @@ public: void SetJumps(int Jumps); void SetSolo(bool Solo); void SetSuper(bool Super); + void SetInvincible(bool Invincible); void SetLiveFrozen(bool Active); void SetDeepFrozen(bool Active); void HandleWeaponSwitch(); diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 3b0356a8e..55164d835 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -3691,6 +3691,7 @@ void CGameContext::RegisterDDRaceCommands() Console()->Register("unninja", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConUnNinja, this, "Removes ninja from you"); Console()->Register("super", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConSuper, this, "Makes you super"); Console()->Register("unsuper", "", CFGFLAG_SERVER, ConUnSuper, this, "Removes super from you"); + Console()->Register("invincible", "?i['0'|'1']", CFGFLAG_SERVER | CMDFLAG_TEST, ConToggleInvincible, this, "Toggles invincible mode"); Console()->Register("endless_hook", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConEndlessHook, this, "Gives you endless hook"); Console()->Register("unendless_hook", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConUnEndlessHook, this, "Removes endless hook from you"); Console()->Register("solo", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConSolo, this, "Puts you into solo part"); @@ -3820,6 +3821,7 @@ void CGameContext::RegisterChatCommands() Console()->Register("unweapons", "", CFGFLAG_CHAT | CMDFLAG_PRACTICE, ConPracticeUnWeapons, this, "Removes all weapons from you"); Console()->Register("ninja", "", CFGFLAG_CHAT | CMDFLAG_PRACTICE, ConPracticeNinja, this, "Makes you a ninja"); Console()->Register("unninja", "", CFGFLAG_CHAT | CMDFLAG_PRACTICE, ConPracticeUnNinja, this, "Removes ninja from you"); + Console()->Register("invincible", "?i['0'|'1']", CFGFLAG_CHAT | CMDFLAG_PRACTICE, ConPracticeToggleInvincible, this, "Toggles invincible mode"); Console()->Register("kill", "", CFGFLAG_CHAT | CFGFLAG_SERVER, ConProtectedKill, this, "Kill yourself when kill-protected during a long game (use f1, kill for regular kill)"); } diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index d25758c53..4d402175d 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -391,6 +391,7 @@ private: static void ConUnLiveFreeze(IConsole::IResult *pResult, void *pUserData); static void ConUnSuper(IConsole::IResult *pResult, void *pUserData); static void ConSuper(IConsole::IResult *pResult, void *pUserData); + static void ConToggleInvincible(IConsole::IResult *pResult, void *pUserData); static void ConShotgun(IConsole::IResult *pResult, void *pUserData); static void ConGrenade(IConsole::IResult *pResult, void *pUserData); static void ConLaser(IConsole::IResult *pResult, void *pUserData); @@ -492,6 +493,7 @@ private: static void ConPracticeUnWeapons(IConsole::IResult *pResult, void *pUserData); static void ConPracticeNinja(IConsole::IResult *pResult, void *pUserData); static void ConPracticeUnNinja(IConsole::IResult *pResult, void *pUserData); + static void ConPracticeToggleInvincible(IConsole::IResult *pResult, void *pUserData); static void ConPracticeAddWeapon(IConsole::IResult *pResult, void *pUserData); static void ConPracticeRemoveWeapon(IConsole::IResult *pResult, void *pUserData);