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));
}
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()
{
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()
{
return GameWorld()->Teams();

View file

@ -51,6 +51,8 @@ public:
void OnPredictedInput(CNetObj_PlayerInput *pNewInput);
void OnDirectInput(CNetObj_PlayerInput *pNewInput);
void ReleaseHook();
void ResetHook();
void ResetInput();
void FireWeapon();
@ -60,6 +62,12 @@ public:
void GiveNinja();
void RemoveNinja();
void ResetVelocity();
void SetVelocity(vec2 NewVelocity);
void SetRawVelocity(vec2 NewVelocity);
void AddVelocity(vec2 Addition);
void ApplyMoveRestrictions();
bool m_IsLocal;
CTeamsCore *TeamsCore();
@ -81,7 +89,6 @@ public:
int m_TileIndex;
int m_TileFIndex;
int m_MoveRestrictions;
bool m_LastRefillJumps;
// Setters/Getters because i don't want to modify vanilla vars access modifiers
@ -91,7 +98,7 @@ public:
void SetActiveWeapon(int ActiveWeap);
CCharacterCore GetCore() { return m_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; }
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; }
@ -145,6 +152,8 @@ private:
int m_ReloadTimer;
int m_AttackTick;
int m_MoveRestrictions;
// these are non-heldback inputs
CNetObj_PlayerInput m_LatestPrevInput;
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
else if(distance(pTarget->m_Pos, m_Pos) > 28)
{
vec2 Temp = pTarget->Core()->m_Vel + (normalize(m_Pos - pTarget->m_Pos) * m_Strength);
pTarget->Core()->m_Vel = ClampVel(pTarget->m_MoveRestrictions, Temp);
pTarget->AddVelocity(normalize(m_Pos - pTarget->m_Pos) * m_Strength);
}
}

View file

@ -47,36 +47,39 @@ bool CLaser::HitCharacter(vec2 From, vec2 To)
m_Energy = -1;
if(m_Type == WEAPON_SHOTGUN)
{
vec2 Temp;
float Strength = GetTuning(m_TuneZone)->m_ShotgunStrength;
float Strength;
if(!m_TuneZone)
Strength = Tuning()->m_ShotgunStrength;
else
Strength = TuningList()[m_TuneZone].m_ShotgunStrength;
const vec2 &HitPos = pHit->Core()->m_Pos;
if(!g_Config.m_SvOldLaser)
{
if(m_PrevPos != HitPos)
{
Temp = pHit->Core()->m_Vel + normalize(m_PrevPos - HitPos) * Strength;
pHit->Core()->m_Vel = ClampVel(pHit->m_MoveRestrictions, Temp);
pHit->AddVelocity(normalize(m_PrevPos - HitPos) * Strength);
}
else
{
pHit->Core()->m_Vel = StackedLaserShotgunBugSpeed;
pHit->SetRawVelocity(StackedLaserShotgunBugSpeed);
}
}
else if(g_Config.m_SvOldLaser && pOwnerChar)
{
if(pOwnerChar->Core()->m_Pos != HitPos)
{
Temp = pHit->Core()->m_Vel + normalize(pOwnerChar->Core()->m_Pos - HitPos) * Strength;
pHit->Core()->m_Vel = ClampVel(pHit->m_MoveRestrictions, Temp);
pHit->AddVelocity(normalize(pOwnerChar->Core()->m_Pos - HitPos) * Strength);
}
else
{
pHit->Core()->m_Vel = StackedLaserShotgunBugSpeed;
pHit->SetRawVelocity(StackedLaserShotgunBugSpeed);
}
}
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)

View file

@ -116,7 +116,7 @@ void CGameWorld::InsertEntity(CEntity *pEnt, bool Last)
if(ID >= 0 && ID < MAX_CLIENTS)
{
m_apCharacters[ID] = pChar;
m_Core.m_apCharacters[ID] = pChar->Core();
m_Core.m_apCharacters[ID] = &pChar->m_Core;
}
pChar->SetCoreWorld(this);
}
@ -309,12 +309,9 @@ void CGameWorld::ReleaseHooked(int ClientID)
CCharacter *pChr = (CCharacter *)CGameWorld::FindFirst(CGameWorld::ENTTYPE_CHARACTER);
for(; pChr; pChr = (CCharacter *)pChr->TypeNext())
{
CCharacterCore *pCore = pChr->Core();
if(pCore->HookedPlayer() == ClientID)
if(pChr->Core()->HookedPlayer() == ClientID && !pChr->IsSuper())
{
pCore->SetHookedPlayer(-1);
pCore->m_HookState = HOOK_RETRACTED;
pCore->m_TriggeredEvents |= COREEVENT_HOOK_RETRACT;
pChr->ReleaseHook();
}
}
}
@ -557,7 +554,7 @@ void CGameWorld::NetObjEnd()
if(pHookedChar->m_MarkedForDestroy)
{
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));
pHookedChar->m_SavedInput.m_TargetY = -1;
pHookedChar->m_KeepHooked = true;
@ -577,7 +574,7 @@ void CGameWorld::NetObjEnd()
if(ID >= 0 && ID < MAX_CLIENTS)
{
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
pSelf->Teleport(pCallingCharacter, Pos);
pCallingCharacter->UnFreeze();
pCallingCharacter->Core()->m_Vel = vec2(0, 0);
pCallingCharacter->ResetVelocity();
pCallingPlayer->m_LastTeleTee.Save(pCallingCharacter);
}
@ -1670,7 +1670,7 @@ void CGameContext::ConTeleXY(IConsole::IResult *pResult, void *pUserData)
// Teleport tee
pSelf->Teleport(pCallingCharacter, Pos);
pCallingCharacter->UnFreeze();
pCallingCharacter->Core()->m_Vel = vec2(0, 0);
pCallingCharacter->ResetVelocity();
pCallingPlayer->m_LastTeleTee.Save(pCallingCharacter);
}
@ -1722,7 +1722,7 @@ void CGameContext::ConTeleCursor(IConsole::IResult *pResult, void *pUserData)
}
pSelf->Teleport(pChr, Pos);
pChr->UnFreeze();
pChr->Core()->m_Vel = vec2(0, 0);
pChr->ResetVelocity();
pPlayer->m_LastTeleTee.Save(pChr);
}

View file

@ -77,8 +77,7 @@ void CGameContext::MoveCharacter(int ClientID, int X, int Y, bool Raw)
if(!pChr)
return;
pChr->Core()->m_Pos.x += ((Raw) ? 1 : 32) * X;
pChr->Core()->m_Pos.y += ((Raw) ? 1 : 32) * Y;
pChr->Move(vec2((Raw ? 1 : 32) * X, Raw ? 1 : 32) * Y);
pChr->m_DDRaceState = DDRACE_CHEAT;
}
@ -348,7 +347,7 @@ void CGameContext::ModifyWeapons(IConsole::IResult *pResult, void *pUserData,
void CGameContext::Teleport(CCharacter *pChr, vec2 Pos)
{
pChr->Core()->m_Pos = Pos;
pChr->SetPosition(Pos);
pChr->m_Pos = Pos;
pChr->m_PrevPos = Pos;
pChr->m_DDRaceState = DDRACE_CHEAT;
@ -412,7 +411,7 @@ void CGameContext::ConTeleport(IConsole::IResult *pResult, void *pUserData)
}
pSelf->Teleport(pChr, Pos);
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);
// 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
{
@ -698,11 +698,16 @@ void CCharacter::OnDirectInput(CNetObj_PlayerInput *pNewInput)
mem_copy(&m_LatestPrevInput, &m_LatestInput, sizeof(m_LatestInput));
}
void CCharacter::ResetHook()
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;
}
@ -1640,7 +1645,7 @@ void CCharacter::HandleTiles(int Index)
m_Core.m_Jumped = 0;
m_Core.m_JumpedTotal = 0;
}
m_Core.m_Vel = ClampVel(m_MoveRestrictions, m_Core.m_Vel);
ApplyMoveRestrictions();
// handle switch tiles
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());
}
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)
{
const int HookedPlayer = m_Core.HookedPlayer();

View file

@ -60,6 +60,7 @@ public:
void OnPredictedInput(CNetObj_PlayerInput *pNewInput);
void OnDirectInput(CNetObj_PlayerInput *pNewInput);
void ReleaseHook();
void ResetHook();
void ResetInput();
void FireWeapon();
@ -88,6 +89,15 @@ public:
class CPlayer *GetPlayer() { return m_pPlayer; }
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:
// player controlling this character
class CPlayer *m_pPlayer;
@ -106,6 +116,8 @@ private:
int m_ReloadTimer;
int m_AttackTick;
int m_MoveRestrictions;
int m_DamageTaken;
int m_EmoteType;
@ -201,8 +213,6 @@ public:
int m_TileIndex;
int m_TileFIndex;
int m_MoveRestrictions;
int64_t m_LastStartWarning;
int64_t m_LastRescue;
bool m_LastRefillJumps;
@ -226,7 +236,7 @@ public:
void SetArmor(int Armor) { m_Armor = Armor; }
CCharacterCore GetCore() { return m_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; }
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; }

View file

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

View file

@ -44,9 +44,9 @@ bool CLaser::HitCharacter(vec2 From, vec2 To)
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)
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
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))
return false;
@ -55,42 +55,39 @@ bool CLaser::HitCharacter(vec2 From, vec2 To)
m_Energy = -1;
if(m_Type == WEAPON_SHOTGUN)
{
vec2 Temp;
float Strength;
if(!m_TuneZone)
Strength = Tuning()->m_ShotgunStrength;
else
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(m_PrevPos != HitPos)
{
Temp = pHit->Core()->m_Vel + normalize(m_PrevPos - HitPos) * Strength;
pHit->Core()->m_Vel = ClampVel(pHit->m_MoveRestrictions, Temp);
pHit->AddVelocity(normalize(m_PrevPos - HitPos) * Strength);
}
else
{
pHit->Core()->m_Vel = StackedLaserShotgunBugSpeed;
pHit->SetRawVelocity(StackedLaserShotgunBugSpeed);
}
}
else if(g_Config.m_SvOldLaser && pOwnerChar)
{
if(pOwnerChar->Core()->m_Pos != HitPos)
{
Temp = pHit->Core()->m_Vel + normalize(pOwnerChar->Core()->m_Pos - HitPos) * Strength;
pHit->Core()->m_Vel = ClampVel(pHit->m_MoveRestrictions, Temp);
pHit->AddVelocity(normalize(pOwnerChar->Core()->m_Pos - HitPos) * Strength);
}
else
{
pHit->Core()->m_Vel = StackedLaserShotgunBugSpeed;
pHit->SetRawVelocity(StackedLaserShotgunBugSpeed);
}
}
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)

View file

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