From 8fc34ae7b94c93fbbff19a85ae608eff6ef7e3c1 Mon Sep 17 00:00:00 2001 From: c0d3d3v Date: Mon, 9 May 2022 13:56:06 +0200 Subject: [PATCH] Add Comments with Specifications to Turrets and Dragger - Make Plasma Bullets explode only once if they hit a character and a obstacle - Make turrets fire speed independend from teams and solo players - Make draggers keep dragging the same tee, also if a closer tee exist --- src/game/server/entities/dragger.cpp | 40 ++++++++++++++------- src/game/server/entities/dragger.h | 20 +++++++++-- src/game/server/entities/dragger_beam.cpp | 1 + src/game/server/entities/dragger_beam.h | 14 ++++++++ src/game/server/entities/gun.cpp | 43 +++++++++++++++-------- src/game/server/entities/gun.h | 20 +++++++++-- src/game/server/entities/plasma.cpp | 34 +++++++++--------- src/game/server/entities/plasma.h | 15 ++++++++ 8 files changed, 138 insertions(+), 49 deletions(-) diff --git a/src/game/server/entities/dragger.cpp b/src/game/server/entities/dragger.cpp index aba1e3526..4a567d9e1 100644 --- a/src/game/server/entities/dragger.cpp +++ b/src/game/server/entities/dragger.cpp @@ -23,6 +23,10 @@ CDragger::CDragger(CGameWorld *pGameWorld, vec2 Pos, float Strength, bool Ignore m_Number = Number; m_EvalTick = Server()->Tick(); + for(auto &TargetId : m_TargetIdInTeam) + { + TargetId = -1; + } mem_zero(m_apDraggerBeam, sizeof(m_apDraggerBeam)); GameWorld()->InsertEntity(this); } @@ -63,12 +67,14 @@ void CDragger::LookForPlayersToDrag() (CEntity **)pPlayersInRange, MAX_CLIENTS, CGameWorld::ENTTYPE_CHARACTER); // The closest player (within range) in a team is selected as the target - int TargetIdInTeam[MAX_CLIENTS]; + int ClosestTargetIdInTeam[MAX_CLIENTS]; + bool CanStillBeTeamTarget[MAX_CLIENTS]; bool IsTarget[MAX_CLIENTS]; int MinDistInTeam[MAX_CLIENTS]; + mem_zero(CanStillBeTeamTarget, sizeof(CanStillBeTeamTarget)); mem_zero(MinDistInTeam, sizeof(MinDistInTeam)); mem_zero(IsTarget, sizeof(IsTarget)); - for(int &TargetId : TargetIdInTeam) + for(int &TargetId : ClosestTargetIdInTeam) { TargetId = -1; } @@ -76,14 +82,16 @@ void CDragger::LookForPlayersToDrag() for(int i = 0; i < NumPlayersInRange; i++) { CCharacter *pTarget = pPlayersInRange[i]; + const int &TargetTeam = pTarget->Team(); + // Do not create a dragger beam for super player - if(pTarget->Team() == TEAM_SUPER) + if(TargetTeam == TEAM_SUPER) { continue; } // If the dragger is disabled for the target's team, no dragger beam will be generated if(m_Layer == LAYER_SWITCH && m_Number > 0 && - !GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[pTarget->Team()]) + !GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[TargetTeam]) { continue; } @@ -95,29 +103,35 @@ void CDragger::LookForPlayersToDrag() !GameServer()->Collision()->IntersectNoLaser(m_Pos, pTarget->m_Pos, 0, 0); if(IsReachable && pTarget->IsAlive()) { + const int &TargetClientId = pTarget->GetPlayer()->GetCID(); // Solo players are dragged independently from the rest of the team - if(pTarget->Teams()->m_Core.GetSolo(pTarget->GetPlayer()->GetCID())) + if(pTarget->Teams()->m_Core.GetSolo(TargetClientId)) { - IsTarget[pTarget->GetPlayer()->GetCID()] = true; + IsTarget[TargetClientId] = true; } else { int Distance = distance(pTarget->m_Pos, m_Pos); - if(MinDistInTeam[pTarget->Team()] == 0 || MinDistInTeam[pTarget->Team()] > Distance) + if(MinDistInTeam[TargetTeam] == 0 || MinDistInTeam[TargetTeam] > Distance) { - MinDistInTeam[pTarget->Team()] = Distance; - TargetIdInTeam[pTarget->Team()] = pTarget->GetPlayer()->GetCID(); + MinDistInTeam[TargetTeam] = Distance; + ClosestTargetIdInTeam[TargetTeam] = TargetClientId; } + CanStillBeTeamTarget[TargetClientId] = true; } } } - // Set the closest player for each team as a target - for(int &TargetId : TargetIdInTeam) + // Set the closest player for each team as a target if the team does not have a target player yet + for(int i = 0; i < MAX_CLIENTS; i++) { - if(TargetId != -1) + if((m_TargetIdInTeam[i] != -1 && !CanStillBeTeamTarget[m_TargetIdInTeam[i]]) || m_TargetIdInTeam[i] == -1) { - IsTarget[TargetId] = true; + m_TargetIdInTeam[i] = ClosestTargetIdInTeam[i]; + } + if(m_TargetIdInTeam[i] != -1) + { + IsTarget[m_TargetIdInTeam[i]] = true; } } diff --git a/src/game/server/entities/dragger.h b/src/game/server/entities/dragger.h index d9ec5b9b6..b49696264 100644 --- a/src/game/server/entities/dragger.h +++ b/src/game/server/entities/dragger.h @@ -5,17 +5,33 @@ #include class CDraggerBeam; +/** + * Draggers generate dragger beams which pull players towards their center similar to a tractor beam + * + * A dragger will only generate one dragger beam per team for the closest player for whom the following criteria are met: + * - The player is within the dragger range (m_SvDraggerRange). + * - The player is not a super player + * - The dragger is activated + * - The dragger beam to be generated is not blocked by laser stoppers (or solid blocks if IgnoreWalls is set to false) + * With the exception of solo players, for whom a dragger beam is always generated, regardless of the rest of the team, + * if the above criteria are met. Solo players have no influence on the generation of the dragger beam for the rest + * of the team. + * A created dragger beam remains for the selected player until one of the criteria is no longer fulfilled. Only then + * can a new dragger beam be created for that team, which may drag another team partner. + */ class CDragger : public CEntity { // m_Core is the direction vector by which a dragger is shifted at each movement tick (every 150ms) vec2 m_Core; float m_Strength; - int m_EvalTick; - void LookForPlayersToDrag(); bool m_IgnoreWalls; + int m_EvalTick; + int m_TargetIdInTeam[MAX_CLIENTS]; CDraggerBeam *m_apDraggerBeam[MAX_CLIENTS]; + void LookForPlayersToDrag(); + public: CDragger(CGameWorld *pGameWorld, vec2 Pos, float Strength, bool IgnoreWalls, int Layer = 0, int Number = 0); diff --git a/src/game/server/entities/dragger_beam.cpp b/src/game/server/entities/dragger_beam.cpp index e0bc382ba..4938d404c 100644 --- a/src/game/server/entities/dragger_beam.cpp +++ b/src/game/server/entities/dragger_beam.cpp @@ -52,6 +52,7 @@ void CDraggerBeam::Tick() Reset(); return; } + // 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); diff --git a/src/game/server/entities/dragger_beam.h b/src/game/server/entities/dragger_beam.h index b488638e7..a30d21577 100644 --- a/src/game/server/entities/dragger_beam.h +++ b/src/game/server/entities/dragger_beam.h @@ -5,6 +5,20 @@ #include "dragger.h" #include +/** + * Dragger beams pull a selected player towards their center + * + * Dragger beams are generated by a particular dragger for the player closest to it and for whom certain criteria are + * met. Dragger beams exist until these criteria are no longer met. Dragger beams dissolve and automatically + * de-register from their dragger source as soon as the player for whom they were created: + * - is no longer alive + * - is no longer in range (m_SvDraggerRange) + * - can no longer be dragged because the beam is intercepted by a laser stopper (or if !IgnoreWalls by solid blocks) + * + * Dragger beams accelerate the selected player every tick towards their center. The length of the speed vector, which + * is added to that of the player, depends only on the strength of the dragger and is between 1 and 3 units long. If + * the player is in the center of the dragger, it will not accelerate. + */ class CDraggerBeam : public CEntity { CDragger *m_pDragger; diff --git a/src/game/server/entities/gun.cpp b/src/game/server/entities/gun.cpp index 19bc1a143..45c3da6cf 100644 --- a/src/game/server/entities/gun.cpp +++ b/src/game/server/entities/gun.cpp @@ -19,9 +19,10 @@ CGun::CGun(CGameWorld *pGameWorld, vec2 Pos, bool Freeze, bool Explosive, int La m_Explosive = Explosive; m_Layer = Layer; m_Number = Number; - m_LastFire = Server()->Tick(); m_EvalTick = Server()->Tick(); + mem_zero(m_LastFireTeam, sizeof(m_LastFireTeam)); + mem_zero(m_LastFireSolo, sizeof(m_LastFireSolo)); GameWorld()->InsertEntity(this); } @@ -38,9 +39,10 @@ void CGun::Tick() } m_Pos += m_Core; } - if(g_Config.m_SvPlasmaPerSec > 0 && - m_LastFire + Server()->TickSpeed() / g_Config.m_SvPlasmaPerSec <= Server()->Tick()) + if(g_Config.m_SvPlasmaPerSec != 0) + { Fire(); + } } void CGun::Fire() @@ -66,14 +68,26 @@ void CGun::Fire() for(int i = 0; i < NumPlayersInRange; i++) { CCharacter *pTarget = pPlayersInRange[i]; + const int &TargetTeam = pTarget->Team(); // Do not fire at super players - if(pTarget->Team() == TEAM_SUPER) + if(TargetTeam == TEAM_SUPER) { continue; } // If the turret is disabled for the target's team, the turret will not fire if(m_Layer == LAYER_SWITCH && m_Number > 0 && - !GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[pTarget->Team()]) + !GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[TargetTeam]) + { + continue; + } + + // Turrets can only shoot at a speed of m_SvPlasmaPerSec + const int &TargetClientId = pTarget->GetPlayer()->GetCID(); + const bool &TargetIsSolo = pTarget->Teams()->m_Core.GetSolo(TargetClientId); + if((TargetIsSolo && + m_LastFireSolo[TargetClientId] + Server()->TickSpeed() / g_Config.m_SvPlasmaPerSec > Server()->Tick()) || + (!TargetIsSolo && + m_LastFireTeam[TargetTeam] + Server()->TickSpeed() / g_Config.m_SvPlasmaPerSec > Server()->Tick())) { continue; } @@ -83,28 +97,30 @@ void CGun::Fire() if(IsReachable && pTarget->IsAlive()) { // Turrets fire on solo players regardless of the rest of the team - if(pTarget->Teams()->m_Core.GetSolo(pTarget->GetPlayer()->GetCID())) + if(TargetIsSolo) { - IsTarget[pTarget->GetPlayer()->GetCID()] = true; + IsTarget[TargetClientId] = true; + m_LastFireSolo[TargetClientId] = Server()->Tick(); } else { int Distance = distance(pTarget->m_Pos, m_Pos); - if(MinDistInTeam[pTarget->Team()] == 0 || MinDistInTeam[pTarget->Team()] > Distance) + if(MinDistInTeam[TargetTeam] == 0 || MinDistInTeam[TargetTeam] > Distance) { - MinDistInTeam[pTarget->Team()] = Distance; - TargetIdInTeam[pTarget->Team()] = pTarget->GetPlayer()->GetCID(); + MinDistInTeam[TargetTeam] = Distance; + TargetIdInTeam[TargetTeam] = TargetClientId; } } } } // Set the closest player for each team as a target - for(int &TargetId : TargetIdInTeam) + for(int i = 0; i < MAX_CLIENTS; i++) { - if(TargetId != -1) + if(TargetIdInTeam[i] != -1) { - IsTarget[TargetId] = true; + IsTarget[TargetIdInTeam[i]] = true; + m_LastFireTeam[i] = Server()->Tick(); } } @@ -115,7 +131,6 @@ void CGun::Fire() { CCharacter *pTarget = GameServer()->GetPlayerChar(i); new CPlasma(&GameServer()->m_World, m_Pos, normalize(pTarget->m_Pos - m_Pos), m_Freeze, m_Explosive, i); - m_LastFire = Server()->Tick(); } } } diff --git a/src/game/server/entities/gun.h b/src/game/server/entities/gun.h index 3964fd7a2..d81451897 100644 --- a/src/game/server/entities/gun.h +++ b/src/game/server/entities/gun.h @@ -6,16 +6,30 @@ #include #include +/** + * Turrets (also referred to as Gun) fire plasma bullets at the nearest player + * + * A turret fires plasma bullets with a certain firing rate (m_SvPlasmaPerSec) at the closest player of a team for whom + * the following criteria are met: + * - The player is within the turret range (m_SvPlasmaRange) + * - The player is not a super player + * - The turret is activated + * - The initial trajectory of the plasma bullet to be generated would not be stopped by any solid block + * With the exception of solo players, for whom plasma bullets will always be fired, regardless of the rest of the team, + * if the above criteria are met. Solo players do not affect the generation of plasma bullets for the rest of the team. + * The shooting rate of m_SvPlasmaPerSec is independent for each team and solo player and starts with the first tick + * when a target player is selected. + */ class CGun : public CEntity { - int m_EvalTick; - vec2 m_Core; bool m_Freeze; bool m_Explosive; + int m_EvalTick; + int m_LastFireTeam[MAX_CLIENTS]; + int m_LastFireSolo[MAX_CLIENTS]; void Fire(); - int m_LastFire; public: CGun(CGameWorld *pGameWorld, vec2 Pos, bool Freeze, bool Explosive, int Layer = 0, int Number = 0); diff --git a/src/game/server/entities/plasma.cpp b/src/game/server/entities/plasma.cpp index 6fe953e22..8eb06466d 100644 --- a/src/game/server/entities/plasma.cpp +++ b/src/game/server/entities/plasma.cpp @@ -29,35 +29,35 @@ CPlasma::CPlasma(CGameWorld *pGameWorld, vec2 Pos, vec2 Dir, bool Freeze, void CPlasma::Tick() { - // A plasma ball has only a limited lifetime + // A plasma bullet has only a limited lifetime if(m_LifeTime == 0) { Reset(); return; } CCharacter *pTarget = GameServer()->GetPlayerChar(m_ForClientID); - // Without a target, a plasma ball has no reason to live + // Without a target, a plasma bullet has no reason to live if(!pTarget) { - // This can allow you to make plasma balls disappear by disconnecting, but this is not a serious cheat Reset(); return; } m_LifeTime--; Move(); - HitCharacter(pTarget); - - // Check if the plasma ball is stopped by a solid block or a laser stopper - int HasIntersection = GameServer()->Collision()->IntersectNoLaser(m_Pos, m_Pos + m_Core, 0, 0); - if(HasIntersection) + if(!HitCharacter(pTarget)) { - if(m_Explosive) + // Check if the plasma bullet is stopped by a solid block or a laser stopper + int HasIntersection = GameServer()->Collision()->IntersectNoLaser(m_Pos, m_Pos + m_Core, 0, 0); + if(HasIntersection) { - // Even in the case of an explosion due to a collision with obstacles, only one player is affected - GameServer()->CreateExplosion( - m_Pos, m_ForClientID, WEAPON_GRENADE, true, pTarget->Team(), pTarget->TeamMask()); + if(m_Explosive) + { + // Even in the case of an explosion due to a collision with obstacles, only one player is affected + GameServer()->CreateExplosion( + m_Pos, m_ForClientID, WEAPON_GRENADE, true, pTarget->Team(), pTarget->TeamMask()); + } + Reset(); } - Reset(); } } @@ -77,7 +77,7 @@ bool CPlasma::HitCharacter(CCharacter *pTarget) return false; } - // Super player should not be able to stop the plasma balls + // Super player should not be able to stop the plasma bullets if(HitPlayer->Team() == TEAM_SUPER) { return false; @@ -91,7 +91,7 @@ bool CPlasma::HitCharacter(CCharacter *pTarget) GameServer()->CreateExplosion( m_Pos, m_ForClientID, WEAPON_GRENADE, true, pTarget->Team(), pTarget->TeamMask()); } - m_MarkedForDestroy = true; + Reset(); return true; } @@ -102,14 +102,14 @@ void CPlasma::Reset() void CPlasma::Snap(int SnappingClient) { - // Only players who can see the targeted player can see the plasma ball + // Only players who can see the targeted player can see the plasma bullet CCharacter *pTarget = GameServer()->GetPlayerChar(m_ForClientID); if(!pTarget->CanSnapCharacter(SnappingClient)) { return; } - // Only players with the plasma ball in their field of view or who want to see everything will receive the snap + // Only players with the plasma bullet in their field of view or who want to see everything will receive the snap if(NetworkClipped(SnappingClient)) return; diff --git a/src/game/server/entities/plasma.h b/src/game/server/entities/plasma.h index 8082736e6..35c8ce6e2 100644 --- a/src/game/server/entities/plasma.h +++ b/src/game/server/entities/plasma.h @@ -4,6 +4,21 @@ #include +/** + * Plasma Bullets are projectiles fired from turrets at a specific target + * + * When hitting a tee, plasma bullets can either freeze or unfreeze the player + * Also, plasma projectiles can explode on impact. However, the player affected by the explosion is not necessarily the + * one the plasma collided with, but if the affected player is not a solo player, then the team-mate with the lowest + * ClientId within the explosion range. Furthermore, the affected player does not feel the explosion at the point of + * impact but at the last position of the plasma bullet. The same applies if a plasma bullet explodes due to a collision + * with a laser stopper or a solid block + * Plasma bullets move every tick in the assigned direction and then accelerate by the factor PLASMA_ACCEL + * Plasma bullets will stop existing as soon as: + * - The player they were created for do no longer exist + * - They have had a collision with a player, a solid block or a laser stopper + * - Their life time of 1.5 seconds has expired + */ class CPlasma : public CEntity { vec2 m_Core;