1/* (c) Shereef Marzouk. See "licence DDRace.txt" and the readme.txt in the root of the distribution for more information. */
2#include "plasma.h"
3#include "character.h"
4
5#include <engine/server.h>
6
7#include <game/generated/protocol.h>
8#include <game/teamscore.h>
9
10#include <game/server/gamecontext.h>
11
12const float PLASMA_ACCEL = 1.1f;
13
14CPlasma::CPlasma(CGameWorld *pGameWorld, vec2 Pos, vec2 Dir, bool Freeze,
15 bool Explosive, int ForClientId) :
16 CEntity(pGameWorld, CGameWorld::ENTTYPE_LASER)
17{
18 m_Pos = Pos;
19 m_Core = Dir;
20 m_Freeze = Freeze;
21 m_Explosive = Explosive;
22 m_ForClientId = ForClientId;
23 m_EvalTick = Server()->Tick();
24 m_LifeTime = Server()->TickSpeed() * 1.5f;
25
26 GameWorld()->InsertEntity(pEntity: this);
27}
28
29void CPlasma::Tick()
30{
31 // A plasma bullet has only a limited lifetime
32 if(m_LifeTime == 0)
33 {
34 Reset();
35 return;
36 }
37 CCharacter *pTarget = GameServer()->GetPlayerChar(ClientId: m_ForClientId);
38 // Without a target, a plasma bullet has no reason to live
39 if(!pTarget)
40 {
41 Reset();
42 return;
43 }
44 m_LifeTime--;
45 Move();
46 HitCharacter(pTarget);
47 // Plasma bullets may explode twice if they would hit both a player and an obstacle in the next move step
48 HitObstacle(pTarget);
49}
50
51void CPlasma::Move()
52{
53 m_Pos += m_Core;
54 m_Core *= PLASMA_ACCEL;
55}
56
57bool CPlasma::HitCharacter(CCharacter *pTarget)
58{
59 vec2 IntersectPos;
60 CCharacter *pHitPlayer = GameServer()->m_World.IntersectCharacter(
61 Pos0: m_Pos, Pos1: m_Pos + m_Core, Radius: 0.0f, NewPos&: IntersectPos, pNotThis: 0, CollideWith: m_ForClientId);
62 if(!pHitPlayer)
63 {
64 return false;
65 }
66
67 // Super player should not be able to stop the plasma bullets
68 if(pHitPlayer->Team() == TEAM_SUPER)
69 {
70 return false;
71 }
72
73 m_Freeze ? pHitPlayer->Freeze() : pHitPlayer->UnFreeze();
74 if(m_Explosive)
75 {
76 // Plasma Turrets are very precise weapons only one tee gets speed from it,
77 // other tees near the explosion remain unaffected
78 GameServer()->CreateExplosion(
79 Pos: m_Pos, Owner: m_ForClientId, Weapon: WEAPON_GRENADE, NoDamage: true, ActivatedTeam: pTarget->Team(), Mask: pTarget->TeamMask());
80 }
81 Reset();
82 return true;
83}
84
85bool CPlasma::HitObstacle(CCharacter *pTarget)
86{
87 // Check if the plasma bullet is stopped by a solid block or a laser stopper
88 int HasIntersection = GameServer()->Collision()->IntersectNoLaser(Pos0: m_Pos, Pos1: m_Pos + m_Core, pOutCollision: 0, pOutBeforeCollision: 0);
89 if(HasIntersection)
90 {
91 if(m_Explosive)
92 {
93 // Even in the case of an explosion due to a collision with obstacles, only one player is affected
94 GameServer()->CreateExplosion(
95 Pos: m_Pos, Owner: m_ForClientId, Weapon: WEAPON_GRENADE, NoDamage: true, ActivatedTeam: pTarget->Team(), Mask: pTarget->TeamMask());
96 }
97 Reset();
98 return true;
99 }
100 return false;
101}
102
103void CPlasma::Reset()
104{
105 m_MarkedForDestroy = true;
106}
107
108void CPlasma::Snap(int SnappingClient)
109{
110 // Only players who can see the targeted player can see the plasma bullet
111 CCharacter *pTarget = GameServer()->GetPlayerChar(ClientId: m_ForClientId);
112 if(!pTarget || !pTarget->CanSnapCharacter(SnappingClient))
113 {
114 return;
115 }
116
117 // Only players with the plasma bullet in their field of view or who want to see everything will receive the snap
118 if(NetworkClipped(SnappingClient))
119 return;
120
121 int SnappingClientVersion = GameServer()->GetClientVersion(ClientId: SnappingClient);
122
123 int Subtype = (m_Explosive ? 1 : 0) | (m_Freeze ? 2 : 0);
124 GameServer()->SnapLaserObject(Context: CSnapContext(SnappingClientVersion), SnapId: GetId(),
125 To: m_Pos, From: m_Pos, StartTick: m_EvalTick, Owner: -1, LaserType: LASERTYPE_PLASMA, Subtype, SwitchNumber: m_Number);
126}
127
128void CPlasma::SwapClients(int Client1, int Client2)
129{
130 m_ForClientId = m_ForClientId == Client1 ? Client2 : m_ForClientId == Client2 ? Client1 : m_ForClientId;
131}
132