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
4#include "character.h"
5
6#include <game/client/laser_data.h>
7#include <game/collision.h>
8#include <game/mapitems.h>
9
10const float PLASMA_ACCEL = 1.1f;
11
12CPlasma::CPlasma(CGameWorld *pGameWorld, int Id, const CLaserData *pData) :
13 CEntity(pGameWorld, CGameWorld::ENTTYPE_PLASMA)
14{
15 m_Id = Id;
16
17 m_Number = pData->m_SwitchNumber;
18 m_Layer = m_Number > 0 ? LAYER_SWITCH : LAYER_GAME;
19 m_LifeTime = (int)(GameWorld()->GameTickSpeed() * 1.5f);
20
21 m_Explosive = false;
22 m_Freeze = false;
23
24 Read(pData);
25
26 CCharacter *pTarget = GameWorld()->GetCharacterById(Id: m_ForClientId);
27 if(!pTarget)
28 {
29 Reset();
30 return;
31 }
32 m_Core = normalize(v: pTarget->m_Pos - m_Pos);
33}
34
35bool CPlasma::Match(const CPlasma *pPlasma) const
36{
37 return pPlasma->m_EvalTick == m_EvalTick && pPlasma->m_Number == m_Number &&
38 pPlasma->m_Explosive == m_Explosive && pPlasma->m_Freeze == m_Freeze && pPlasma->m_ForClientId == m_ForClientId;
39}
40
41void CPlasma::Read(const CLaserData *pData)
42{
43 m_Pos = pData->m_From;
44 m_EvalTick = pData->m_StartTick;
45 m_ForClientId = pData->m_Owner;
46
47 if(0 <= pData->m_Subtype && pData->m_Subtype < NUM_LASERGUNTYPES)
48 {
49 m_Explosive = (pData->m_Subtype & 1);
50 m_Freeze = (pData->m_Subtype & 2);
51 }
52}
53
54void CPlasma::Reset()
55{
56 m_MarkedForDestroy = true;
57}
58
59void CPlasma::Tick()
60{
61 // A plasma bullet has only a limited lifetime
62 if(m_LifeTime == 0)
63 {
64 Reset();
65 return;
66 }
67 CCharacter *pTarget = GameWorld()->GetCharacterById(Id: m_ForClientId);
68 // Without a target, a plasma bullet has no reason to live
69 if(!pTarget)
70 {
71 Reset();
72 return;
73 }
74 m_LifeTime--;
75 Move();
76 HitCharacter(pTarget);
77 // Plasma bullets may explode twice if they would hit both a player and an obstacle in the next move step
78 HitObstacle(pTarget);
79}
80
81void CPlasma::Move()
82{
83 m_Pos += m_Core;
84 m_Core *= PLASMA_ACCEL;
85}
86
87bool CPlasma::HitCharacter(CCharacter *pTarget)
88{
89 vec2 IntersectPos;
90 CCharacter *pHitPlayer = GameWorld()->IntersectCharacter(
91 Pos0: m_Pos, Pos1: m_Pos + m_Core, Radius: 0.0f, NewPos&: IntersectPos, pNotThis: nullptr, CollideWith: m_ForClientId);
92 if(!pHitPlayer)
93 {
94 return false;
95 }
96
97 // Super player should not be able to stop the plasma bullets
98 if(pHitPlayer->Team() == TEAM_SUPER)
99 {
100 return false;
101 }
102
103 m_Freeze ? pHitPlayer->Freeze() : pHitPlayer->UnFreeze();
104 if(m_Explosive)
105 {
106 // Plasma Turrets are very precise weapons only one tee gets speed from it,
107 // other tees near the explosion remain unaffected
108 GameWorld()->CreateExplosion(
109 Pos: m_Pos, Owner: m_ForClientId, Weapon: WEAPON_GRENADE, NoDamage: true, ActivatedTeam: pTarget->Team(), Mask: CClientMask().set());
110 }
111 Reset();
112 return true;
113}
114
115bool CPlasma::HitObstacle(CCharacter *pTarget)
116{
117 // Check if the plasma bullet is stopped by a solid block or a laser stopper
118 int HasIntersection = Collision()->IntersectNoLaser(Pos0: m_Pos, Pos1: m_Pos + m_Core, pOutCollision: nullptr, pOutBeforeCollision: nullptr);
119 if(HasIntersection)
120 {
121 if(m_Explosive)
122 {
123 // Even in the case of an explosion due to a collision with obstacles, only one player is affected
124 GameWorld()->CreateExplosion(
125 Pos: m_Pos, Owner: m_ForClientId, Weapon: WEAPON_GRENADE, NoDamage: true, ActivatedTeam: pTarget->Team(), Mask: CClientMask().set());
126 }
127 Reset();
128 return true;
129 }
130 return false;
131}
132