Character: Incapsulate m_MoveRestrictions and m_Core

Instead of leaking the m_MoveRestrictions to all classes which needs to
adjust the Character velocity, add a setter which takes into account those
restrictions.

The shotgun bug replication requires an access to the Velocity without the
restrictions applied (so we have to have this dirty setter).

Expose only *const* CCharacterCore to force the setters usage and prevent
incorrect write.
This commit is contained in:
Alexander Akulich 2024-01-19 22:05:53 +03:00
parent 432b4319f6
commit 386935f5cf
12 changed files with 145 additions and 53 deletions

View file

@ -546,6 +546,19 @@ void CCharacter::OnDirectInput(CNetObj_PlayerInput *pNewInput)
mem_copy(&m_LatestPrevInput, &m_LatestInput, sizeof(m_LatestInput)); mem_copy(&m_LatestPrevInput, &m_LatestInput, sizeof(m_LatestInput));
} }
void CCharacter::ReleaseHook()
{
m_Core.SetHookedPlayer(-1);
m_Core.m_HookState = HOOK_RETRACTED;
m_Core.m_TriggeredEvents |= COREEVENT_HOOK_RETRACT;
}
void CCharacter::ResetHook()
{
ReleaseHook();
m_Core.m_HookPos = m_Core.m_Pos;
}
void CCharacter::ResetInput() void CCharacter::ResetInput()
{ {
m_Input.m_Direction = 0; m_Input.m_Direction = 0;
@ -1105,6 +1118,33 @@ void CCharacter::GiveAllWeapons()
} }
} }
void CCharacter::ResetVelocity()
{
m_Core.m_Vel = vec2(0, 0);
}
// The method is needed only to reproduce 'shotgun bug' ddnet#5258
// Use SetVelocity() instead.
void CCharacter::SetVelocity(const vec2 NewVelocity)
{
m_Core.m_Vel = ClampVel(m_MoveRestrictions, NewVelocity);
}
void CCharacter::SetRawVelocity(const vec2 NewVelocity)
{
m_Core.m_Vel = NewVelocity;
}
void CCharacter::AddVelocity(const vec2 Addition)
{
SetVelocity(m_Core.m_Vel + Addition);
}
void CCharacter::ApplyMoveRestrictions()
{
m_Core.m_Vel = ClampVel(m_MoveRestrictions, m_Core.m_Vel);
}
CTeamsCore *CCharacter::TeamsCore() CTeamsCore *CCharacter::TeamsCore()
{ {
return GameWorld()->Teams(); return GameWorld()->Teams();

View file

@ -51,6 +51,8 @@ public:
void OnPredictedInput(CNetObj_PlayerInput *pNewInput); void OnPredictedInput(CNetObj_PlayerInput *pNewInput);
void OnDirectInput(CNetObj_PlayerInput *pNewInput); void OnDirectInput(CNetObj_PlayerInput *pNewInput);
void ReleaseHook();
void ResetHook();
void ResetInput(); void ResetInput();
void FireWeapon(); void FireWeapon();
@ -60,6 +62,12 @@ public:
void GiveNinja(); void GiveNinja();
void RemoveNinja(); void RemoveNinja();
void ResetVelocity();
void SetVelocity(vec2 NewVelocity);
void SetRawVelocity(vec2 NewVelocity);
void AddVelocity(vec2 Addition);
void ApplyMoveRestrictions();
bool m_IsLocal; bool m_IsLocal;
CTeamsCore *TeamsCore(); CTeamsCore *TeamsCore();
@ -81,7 +89,6 @@ public:
int m_TileIndex; int m_TileIndex;
int m_TileFIndex; int m_TileFIndex;
int m_MoveRestrictions;
bool m_LastRefillJumps; bool m_LastRefillJumps;
// Setters/Getters because i don't want to modify vanilla vars access modifiers // Setters/Getters because i don't want to modify vanilla vars access modifiers
@ -91,7 +98,7 @@ public:
void SetActiveWeapon(int ActiveWeap); void SetActiveWeapon(int ActiveWeap);
CCharacterCore GetCore() { return m_Core; } CCharacterCore GetCore() { return m_Core; }
void SetCore(CCharacterCore Core) { m_Core = Core; } void SetCore(CCharacterCore Core) { m_Core = Core; }
CCharacterCore *Core() { return &m_Core; } const CCharacterCore *Core() const { return &m_Core; }
bool GetWeaponGot(int Type) { return m_Core.m_aWeapons[Type].m_Got; } bool GetWeaponGot(int Type) { return m_Core.m_aWeapons[Type].m_Got; }
void SetWeaponGot(int Type, bool Value) { m_Core.m_aWeapons[Type].m_Got = Value; } void SetWeaponGot(int Type, bool Value) { m_Core.m_aWeapons[Type].m_Got = Value; }
int GetWeaponAmmo(int Type) { return m_Core.m_aWeapons[Type].m_Ammo; } int GetWeaponAmmo(int Type) { return m_Core.m_aWeapons[Type].m_Ammo; }
@ -145,6 +152,8 @@ private:
int m_ReloadTimer; int m_ReloadTimer;
int m_AttackTick; int m_AttackTick;
int m_MoveRestrictions;
// these are non-heldback inputs // these are non-heldback inputs
CNetObj_PlayerInput m_LatestPrevInput; CNetObj_PlayerInput m_LatestPrevInput;
CNetObj_PlayerInput m_LatestInput; CNetObj_PlayerInput m_LatestInput;

View file

@ -124,8 +124,7 @@ void CDragger::DraggerBeamTick()
// In the center of the dragger a tee does not experience speed-up // In the center of the dragger a tee does not experience speed-up
else if(distance(pTarget->m_Pos, m_Pos) > 28) else if(distance(pTarget->m_Pos, m_Pos) > 28)
{ {
vec2 Temp = pTarget->Core()->m_Vel + (normalize(m_Pos - pTarget->m_Pos) * m_Strength); pTarget->AddVelocity(normalize(m_Pos - pTarget->m_Pos) * m_Strength);
pTarget->Core()->m_Vel = ClampVel(pTarget->m_MoveRestrictions, Temp);
} }
} }

View file

@ -47,36 +47,39 @@ bool CLaser::HitCharacter(vec2 From, vec2 To)
m_Energy = -1; m_Energy = -1;
if(m_Type == WEAPON_SHOTGUN) if(m_Type == WEAPON_SHOTGUN)
{ {
vec2 Temp; float Strength;
float Strength = GetTuning(m_TuneZone)->m_ShotgunStrength; if(!m_TuneZone)
Strength = Tuning()->m_ShotgunStrength;
else
Strength = TuningList()[m_TuneZone].m_ShotgunStrength;
const vec2 &HitPos = pHit->Core()->m_Pos; const vec2 &HitPos = pHit->Core()->m_Pos;
if(!g_Config.m_SvOldLaser) if(!g_Config.m_SvOldLaser)
{ {
if(m_PrevPos != HitPos) if(m_PrevPos != HitPos)
{ {
Temp = pHit->Core()->m_Vel + normalize(m_PrevPos - HitPos) * Strength; pHit->AddVelocity(normalize(m_PrevPos - HitPos) * Strength);
pHit->Core()->m_Vel = ClampVel(pHit->m_MoveRestrictions, Temp);
} }
else else
{ {
pHit->Core()->m_Vel = StackedLaserShotgunBugSpeed; pHit->SetRawVelocity(StackedLaserShotgunBugSpeed);
} }
} }
else if(g_Config.m_SvOldLaser && pOwnerChar) else if(g_Config.m_SvOldLaser && pOwnerChar)
{ {
if(pOwnerChar->Core()->m_Pos != HitPos) if(pOwnerChar->Core()->m_Pos != HitPos)
{ {
Temp = pHit->Core()->m_Vel + normalize(pOwnerChar->Core()->m_Pos - HitPos) * Strength; pHit->AddVelocity(normalize(pOwnerChar->Core()->m_Pos - HitPos) * Strength);
pHit->Core()->m_Vel = ClampVel(pHit->m_MoveRestrictions, Temp);
} }
else else
{ {
pHit->Core()->m_Vel = StackedLaserShotgunBugSpeed; pHit->SetRawVelocity(StackedLaserShotgunBugSpeed);
} }
} }
else else
{ {
pHit->Core()->m_Vel = ClampVel(pHit->m_MoveRestrictions, pHit->Core()->m_Vel); // Re-apply move restrictions as a part of 'shotgun bug' reproduction
pHit->ApplyMoveRestrictions();
} }
} }
else if(m_Type == WEAPON_LASER) else if(m_Type == WEAPON_LASER)

View file

@ -116,7 +116,7 @@ void CGameWorld::InsertEntity(CEntity *pEnt, bool Last)
if(ID >= 0 && ID < MAX_CLIENTS) if(ID >= 0 && ID < MAX_CLIENTS)
{ {
m_apCharacters[ID] = pChar; m_apCharacters[ID] = pChar;
m_Core.m_apCharacters[ID] = pChar->Core(); m_Core.m_apCharacters[ID] = &pChar->m_Core;
} }
pChar->SetCoreWorld(this); pChar->SetCoreWorld(this);
} }
@ -309,12 +309,9 @@ void CGameWorld::ReleaseHooked(int ClientID)
CCharacter *pChr = (CCharacter *)CGameWorld::FindFirst(CGameWorld::ENTTYPE_CHARACTER); CCharacter *pChr = (CCharacter *)CGameWorld::FindFirst(CGameWorld::ENTTYPE_CHARACTER);
for(; pChr; pChr = (CCharacter *)pChr->TypeNext()) for(; pChr; pChr = (CCharacter *)pChr->TypeNext())
{ {
CCharacterCore *pCore = pChr->Core(); if(pChr->Core()->HookedPlayer() == ClientID && !pChr->IsSuper())
if(pCore->HookedPlayer() == ClientID)
{ {
pCore->SetHookedPlayer(-1); pChr->ReleaseHook();
pCore->m_HookState = HOOK_RETRACTED;
pCore->m_TriggeredEvents |= COREEVENT_HOOK_RETRACT;
} }
} }
} }
@ -557,7 +554,7 @@ void CGameWorld::NetObjEnd()
if(pHookedChar->m_MarkedForDestroy) if(pHookedChar->m_MarkedForDestroy)
{ {
pHookedChar->m_Pos = pHookedChar->m_Core.m_Pos = pChar->m_Core.m_HookPos; pHookedChar->m_Pos = pHookedChar->m_Core.m_Pos = pChar->m_Core.m_HookPos;
pHookedChar->m_Core.m_Vel = vec2(0, 0); pHookedChar->ResetVelocity();
mem_zero(&pHookedChar->m_SavedInput, sizeof(pHookedChar->m_SavedInput)); mem_zero(&pHookedChar->m_SavedInput, sizeof(pHookedChar->m_SavedInput));
pHookedChar->m_SavedInput.m_TargetY = -1; pHookedChar->m_SavedInput.m_TargetY = -1;
pHookedChar->m_KeepHooked = true; pHookedChar->m_KeepHooked = true;
@ -577,7 +574,7 @@ void CGameWorld::NetObjEnd()
if(ID >= 0 && ID < MAX_CLIENTS) if(ID >= 0 && ID < MAX_CLIENTS)
{ {
m_apCharacters[ID] = pChar; m_apCharacters[ID] = pChar;
m_Core.m_apCharacters[ID] = pChar->Core(); m_Core.m_apCharacters[ID] = &pChar->m_Core;
} }
} }
} }

View file

@ -1593,7 +1593,7 @@ void CGameContext::ConTeleTo(IConsole::IResult *pResult, void *pUserData)
// Teleport tee // Teleport tee
pSelf->Teleport(pCallingCharacter, Pos); pSelf->Teleport(pCallingCharacter, Pos);
pCallingCharacter->UnFreeze(); pCallingCharacter->UnFreeze();
pCallingCharacter->Core()->m_Vel = vec2(0, 0); pCallingCharacter->ResetVelocity();
pCallingPlayer->m_LastTeleTee.Save(pCallingCharacter); pCallingPlayer->m_LastTeleTee.Save(pCallingCharacter);
} }
@ -1670,7 +1670,7 @@ void CGameContext::ConTeleXY(IConsole::IResult *pResult, void *pUserData)
// Teleport tee // Teleport tee
pSelf->Teleport(pCallingCharacter, Pos); pSelf->Teleport(pCallingCharacter, Pos);
pCallingCharacter->UnFreeze(); pCallingCharacter->UnFreeze();
pCallingCharacter->Core()->m_Vel = vec2(0, 0); pCallingCharacter->ResetVelocity();
pCallingPlayer->m_LastTeleTee.Save(pCallingCharacter); pCallingPlayer->m_LastTeleTee.Save(pCallingCharacter);
} }
@ -1722,7 +1722,7 @@ void CGameContext::ConTeleCursor(IConsole::IResult *pResult, void *pUserData)
} }
pSelf->Teleport(pChr, Pos); pSelf->Teleport(pChr, Pos);
pChr->UnFreeze(); pChr->UnFreeze();
pChr->Core()->m_Vel = vec2(0, 0); pChr->ResetVelocity();
pPlayer->m_LastTeleTee.Save(pChr); pPlayer->m_LastTeleTee.Save(pChr);
} }

View file

@ -77,8 +77,7 @@ void CGameContext::MoveCharacter(int ClientID, int X, int Y, bool Raw)
if(!pChr) if(!pChr)
return; return;
pChr->Core()->m_Pos.x += ((Raw) ? 1 : 32) * X; pChr->Move(vec2((Raw ? 1 : 32) * X, Raw ? 1 : 32) * Y);
pChr->Core()->m_Pos.y += ((Raw) ? 1 : 32) * Y;
pChr->m_DDRaceState = DDRACE_CHEAT; pChr->m_DDRaceState = DDRACE_CHEAT;
} }
@ -348,7 +347,7 @@ void CGameContext::ModifyWeapons(IConsole::IResult *pResult, void *pUserData,
void CGameContext::Teleport(CCharacter *pChr, vec2 Pos) void CGameContext::Teleport(CCharacter *pChr, vec2 Pos)
{ {
pChr->Core()->m_Pos = Pos; pChr->SetPosition(Pos);
pChr->m_Pos = Pos; pChr->m_Pos = Pos;
pChr->m_PrevPos = Pos; pChr->m_PrevPos = Pos;
pChr->m_DDRaceState = DDRACE_CHEAT; pChr->m_DDRaceState = DDRACE_CHEAT;
@ -412,7 +411,7 @@ void CGameContext::ConTeleport(IConsole::IResult *pResult, void *pUserData)
} }
pSelf->Teleport(pChr, Pos); pSelf->Teleport(pChr, Pos);
pChr->UnFreeze(); pChr->UnFreeze();
pChr->Core()->m_Vel = vec2(0, 0); pChr->SetVelocity(vec2(0, 0));
} }
} }

View file

@ -262,7 +262,7 @@ void CCharacter::HandleNinja()
Collision()->MoveBox(&m_Core.m_Pos, &m_Core.m_Vel, vec2(GetProximityRadius(), GetProximityRadius()), GroundElasticity); Collision()->MoveBox(&m_Core.m_Pos, &m_Core.m_Vel, vec2(GetProximityRadius(), GetProximityRadius()), GroundElasticity);
// reset velocity so the client doesn't predict stuff // reset velocity so the client doesn't predict stuff
m_Core.m_Vel = vec2(0.f, 0.f); ResetVelocity();
// check if we Hit anything along the way // check if we Hit anything along the way
{ {
@ -698,11 +698,16 @@ void CCharacter::OnDirectInput(CNetObj_PlayerInput *pNewInput)
mem_copy(&m_LatestPrevInput, &m_LatestInput, sizeof(m_LatestInput)); mem_copy(&m_LatestPrevInput, &m_LatestInput, sizeof(m_LatestInput));
} }
void CCharacter::ResetHook() void CCharacter::ReleaseHook()
{ {
m_Core.SetHookedPlayer(-1); m_Core.SetHookedPlayer(-1);
m_Core.m_HookState = HOOK_RETRACTED; m_Core.m_HookState = HOOK_RETRACTED;
m_Core.m_TriggeredEvents |= COREEVENT_HOOK_RETRACT; m_Core.m_TriggeredEvents |= COREEVENT_HOOK_RETRACT;
}
void CCharacter::ResetHook()
{
ReleaseHook();
m_Core.m_HookPos = m_Core.m_Pos; m_Core.m_HookPos = m_Core.m_Pos;
} }
@ -1640,7 +1645,7 @@ void CCharacter::HandleTiles(int Index)
m_Core.m_Jumped = 0; m_Core.m_Jumped = 0;
m_Core.m_JumpedTotal = 0; m_Core.m_JumpedTotal = 0;
} }
m_Core.m_Vel = ClampVel(m_MoveRestrictions, m_Core.m_Vel); ApplyMoveRestrictions();
// handle switch tiles // handle switch tiles
if(Collision()->GetSwitchType(MapIndex) == TILE_SWITCHOPEN && Team() != TEAM_SUPER && Collision()->GetSwitchNumber(MapIndex) > 0) if(Collision()->GetSwitchType(MapIndex) == TILE_SWITCHOPEN && Team() != TEAM_SUPER && Collision()->GetSwitchNumber(MapIndex) > 0)
@ -2357,6 +2362,43 @@ CClientMask CCharacter::TeamMask()
return Teams()->TeamMask(Team(), -1, GetPlayer()->GetCID()); return Teams()->TeamMask(Team(), -1, GetPlayer()->GetCID());
} }
void CCharacter::SetPosition(const vec2 &Position)
{
m_Core.m_Pos = Position;
}
void CCharacter::Move(vec2 RelPos)
{
m_Core.m_Pos += RelPos;
}
void CCharacter::ResetVelocity()
{
m_Core.m_Vel = vec2(0, 0);
}
void CCharacter::SetVelocity(vec2 NewVelocity)
{
m_Core.m_Vel = ClampVel(m_MoveRestrictions, NewVelocity);
}
// The method is needed only to reproduce 'shotgun bug' ddnet#5258
// Use SetVelocity() instead.
void CCharacter::SetRawVelocity(vec2 NewVelocity)
{
m_Core.m_Vel = NewVelocity;
}
void CCharacter::AddVelocity(vec2 Addition)
{
SetVelocity(m_Core.m_Vel + Addition);
}
void CCharacter::ApplyMoveRestrictions()
{
m_Core.m_Vel = ClampVel(m_MoveRestrictions, m_Core.m_Vel);
}
void CCharacter::SwapClients(int Client1, int Client2) void CCharacter::SwapClients(int Client1, int Client2)
{ {
const int HookedPlayer = m_Core.HookedPlayer(); const int HookedPlayer = m_Core.HookedPlayer();

View file

@ -60,6 +60,7 @@ public:
void OnPredictedInput(CNetObj_PlayerInput *pNewInput); void OnPredictedInput(CNetObj_PlayerInput *pNewInput);
void OnDirectInput(CNetObj_PlayerInput *pNewInput); void OnDirectInput(CNetObj_PlayerInput *pNewInput);
void ReleaseHook();
void ResetHook(); void ResetHook();
void ResetInput(); void ResetInput();
void FireWeapon(); void FireWeapon();
@ -88,6 +89,15 @@ public:
class CPlayer *GetPlayer() { return m_pPlayer; } class CPlayer *GetPlayer() { return m_pPlayer; }
CClientMask TeamMask(); CClientMask TeamMask();
void SetPosition(const vec2 &Position);
void Move(vec2 RelPos);
void ResetVelocity();
void SetVelocity(vec2 NewVelocity);
void SetRawVelocity(vec2 NewVelocity);
void AddVelocity(vec2 Addition);
void ApplyMoveRestrictions();
private: private:
// player controlling this character // player controlling this character
class CPlayer *m_pPlayer; class CPlayer *m_pPlayer;
@ -106,6 +116,8 @@ private:
int m_ReloadTimer; int m_ReloadTimer;
int m_AttackTick; int m_AttackTick;
int m_MoveRestrictions;
int m_DamageTaken; int m_DamageTaken;
int m_EmoteType; int m_EmoteType;
@ -201,8 +213,6 @@ public:
int m_TileIndex; int m_TileIndex;
int m_TileFIndex; int m_TileFIndex;
int m_MoveRestrictions;
int64_t m_LastStartWarning; int64_t m_LastStartWarning;
int64_t m_LastRescue; int64_t m_LastRescue;
bool m_LastRefillJumps; bool m_LastRefillJumps;
@ -226,7 +236,7 @@ public:
void SetArmor(int Armor) { m_Armor = Armor; } void SetArmor(int Armor) { m_Armor = Armor; }
CCharacterCore GetCore() { return m_Core; } CCharacterCore GetCore() { return m_Core; }
void SetCore(CCharacterCore Core) { m_Core = Core; } void SetCore(CCharacterCore Core) { m_Core = Core; }
CCharacterCore *Core() { return &m_Core; } const CCharacterCore *Core() const { return &m_Core; }
bool GetWeaponGot(int Type) { return m_Core.m_aWeapons[Type].m_Got; } bool GetWeaponGot(int Type) { return m_Core.m_aWeapons[Type].m_Got; }
void SetWeaponGot(int Type, bool Value) { m_Core.m_aWeapons[Type].m_Got = Value; } void SetWeaponGot(int Type, bool Value) { m_Core.m_aWeapons[Type].m_Got = Value; }
int GetWeaponAmmo(int Type) { return m_Core.m_aWeapons[Type].m_Ammo; } int GetWeaponAmmo(int Type) { return m_Core.m_aWeapons[Type].m_Ammo; }

View file

@ -71,8 +71,7 @@ void CDraggerBeam::Tick()
// In the center of the dragger a tee does not experience speed-up // In the center of the dragger a tee does not experience speed-up
else if(distance(pTarget->m_Pos, m_Pos) > 28) else if(distance(pTarget->m_Pos, m_Pos) > 28)
{ {
vec2 Temp = pTarget->Core()->m_Vel + (normalize(m_Pos - pTarget->m_Pos) * m_Strength); pTarget->AddVelocity(normalize(m_Pos - pTarget->m_Pos) * m_Strength);
pTarget->Core()->m_Vel = ClampVel(pTarget->m_MoveRestrictions, Temp);
} }
} }

View file

@ -44,9 +44,9 @@ bool CLaser::HitCharacter(vec2 From, vec2 To)
bool pDontHitSelf = g_Config.m_SvOldLaser || (m_Bounces == 0 && !m_WasTele); bool pDontHitSelf = g_Config.m_SvOldLaser || (m_Bounces == 0 && !m_WasTele);
if(pOwnerChar ? (!pOwnerChar->LaserHitDisabled() && m_Type == WEAPON_LASER) || (!pOwnerChar->ShotgunHitDisabled() && m_Type == WEAPON_SHOTGUN) : g_Config.m_SvHit) if(pOwnerChar ? (!pOwnerChar->LaserHitDisabled() && m_Type == WEAPON_LASER) || (!pOwnerChar->ShotgunHitDisabled() && m_Type == WEAPON_SHOTGUN) : g_Config.m_SvHit)
pHit = GameServer()->m_World.IntersectCharacter(m_Pos, To, 0.f, At, pDontHitSelf ? pOwnerChar : 0, m_Owner); pHit = GameWorld()->IntersectCharacter(m_Pos, To, 0.f, At, pDontHitSelf ? pOwnerChar : 0, m_Owner);
else else
pHit = GameServer()->m_World.IntersectCharacter(m_Pos, To, 0.f, At, pDontHitSelf ? pOwnerChar : 0, m_Owner, pOwnerChar); pHit = GameWorld()->IntersectCharacter(m_Pos, To, 0.f, At, pDontHitSelf ? pOwnerChar : 0, m_Owner, pOwnerChar);
if(!pHit || (pHit == pOwnerChar && g_Config.m_SvOldLaser) || (pHit != pOwnerChar && pOwnerChar ? (pOwnerChar->LaserHitDisabled() && m_Type == WEAPON_LASER) || (pOwnerChar->ShotgunHitDisabled() && m_Type == WEAPON_SHOTGUN) : !g_Config.m_SvHit)) if(!pHit || (pHit == pOwnerChar && g_Config.m_SvOldLaser) || (pHit != pOwnerChar && pOwnerChar ? (pOwnerChar->LaserHitDisabled() && m_Type == WEAPON_LASER) || (pOwnerChar->ShotgunHitDisabled() && m_Type == WEAPON_SHOTGUN) : !g_Config.m_SvHit))
return false; return false;
@ -55,42 +55,39 @@ bool CLaser::HitCharacter(vec2 From, vec2 To)
m_Energy = -1; m_Energy = -1;
if(m_Type == WEAPON_SHOTGUN) if(m_Type == WEAPON_SHOTGUN)
{ {
vec2 Temp;
float Strength; float Strength;
if(!m_TuneZone) if(!m_TuneZone)
Strength = Tuning()->m_ShotgunStrength; Strength = Tuning()->m_ShotgunStrength;
else else
Strength = TuningList()[m_TuneZone].m_ShotgunStrength; Strength = TuningList()[m_TuneZone].m_ShotgunStrength;
vec2 &HitPos = pHit->Core()->m_Pos; const vec2 &HitPos = pHit->Core()->m_Pos;
if(!g_Config.m_SvOldLaser) if(!g_Config.m_SvOldLaser)
{ {
if(m_PrevPos != HitPos) if(m_PrevPos != HitPos)
{ {
Temp = pHit->Core()->m_Vel + normalize(m_PrevPos - HitPos) * Strength; pHit->AddVelocity(normalize(m_PrevPos - HitPos) * Strength);
pHit->Core()->m_Vel = ClampVel(pHit->m_MoveRestrictions, Temp);
} }
else else
{ {
pHit->Core()->m_Vel = StackedLaserShotgunBugSpeed; pHit->SetRawVelocity(StackedLaserShotgunBugSpeed);
} }
} }
else if(g_Config.m_SvOldLaser && pOwnerChar) else if(g_Config.m_SvOldLaser && pOwnerChar)
{ {
if(pOwnerChar->Core()->m_Pos != HitPos) if(pOwnerChar->Core()->m_Pos != HitPos)
{ {
Temp = pHit->Core()->m_Vel + normalize(pOwnerChar->Core()->m_Pos - HitPos) * Strength; pHit->AddVelocity(normalize(pOwnerChar->Core()->m_Pos - HitPos) * Strength);
pHit->Core()->m_Vel = ClampVel(pHit->m_MoveRestrictions, Temp);
} }
else else
{ {
pHit->Core()->m_Vel = StackedLaserShotgunBugSpeed; pHit->SetRawVelocity(StackedLaserShotgunBugSpeed);
} }
} }
else else
{ {
pHit->Core()->m_Vel = ClampVel(pHit->m_MoveRestrictions, pHit->Core()->m_Vel); // Re-apply move restrictions as a part of 'shotgun bug' reproduction
pHit->ApplyMoveRestrictions();
} }
} }
else if(m_Type == WEAPON_LASER) else if(m_Type == WEAPON_LASER)

View file

@ -361,12 +361,9 @@ void CGameWorld::ReleaseHooked(int ClientID)
CCharacter *pChr = (CCharacter *)CGameWorld::FindFirst(CGameWorld::ENTTYPE_CHARACTER); CCharacter *pChr = (CCharacter *)CGameWorld::FindFirst(CGameWorld::ENTTYPE_CHARACTER);
for(; pChr; pChr = (CCharacter *)pChr->TypeNext()) for(; pChr; pChr = (CCharacter *)pChr->TypeNext())
{ {
CCharacterCore *pCore = pChr->Core(); if(pChr->Core()->HookedPlayer() == ClientID && !pChr->IsSuper())
if(pCore->HookedPlayer() == ClientID && !pChr->IsSuper())
{ {
pCore->SetHookedPlayer(-1); pChr->ReleaseHook();
pCore->m_TriggeredEvents |= COREEVENT_HOOK_RETRACT;
pCore->m_HookState = HOOK_RETRACTED;
} }
} }
} }