1/* (c) Shereef Marzouk. See "licence DDRace.txt" and the readme.txt in the root of the distribution for more information. */
2#include "dragger.h"
3
4#include "character.h"
5
6#include <engine/shared/config.h>
7
8#include <generated/protocol.h>
9
10#include <game/client/laser_data.h>
11#include <game/collision.h>
12#include <game/mapitems.h>
13
14void CDragger::Tick()
15{
16 if(GameWorld()->GameTick() % (int)(GameWorld()->GameTickSpeed() * 0.15f) == 0)
17 {
18 Collision()->MoverSpeed(x: m_Pos.x, y: m_Pos.y, pSpeed: &m_Core);
19 m_Pos += m_Core;
20
21 LookForPlayersToDrag();
22 }
23
24 DraggerBeamTick();
25}
26
27void CDragger::LookForPlayersToDrag()
28{
29 // Create a list of players who are in the range of the dragger
30 CEntity *apPlayersInRange[MAX_CLIENTS];
31 std::fill(first: std::begin(arr&: apPlayersInRange), last: std::end(arr&: apPlayersInRange), value: nullptr);
32
33 int NumPlayersInRange = GameWorld()->FindEntities(Pos: m_Pos,
34 Radius: g_Config.m_SvDraggerRange - CCharacterCore::PhysicalSize(),
35 ppEnts: apPlayersInRange, Max: MAX_CLIENTS, Type: CGameWorld::ENTTYPE_CHARACTER);
36
37 // The closest player (within range) in a team is selected as the target
38 int ClosestTargetId = -1;
39 bool CanStillBeTeamTarget = false;
40 int MinDistInTeam = 0;
41
42 for(int i = 0; i < NumPlayersInRange; i++)
43 {
44 CCharacter *pTarget = static_cast<CCharacter *>(apPlayersInRange[i]);
45 const int &TargetTeam = pTarget->Team();
46
47 // Do not create a dragger beam for super player
48 if(TargetTeam == TEAM_SUPER)
49 {
50 continue;
51 }
52 // If the dragger is disabled for the target's team, no dragger beam will be generated
53 if(m_Layer == LAYER_SWITCH && m_Number > 0 &&
54 !Switchers()[m_Number].m_aStatus[TargetTeam])
55 {
56 continue;
57 }
58
59 // Dragger beams can be created only for reachable, alive players
60 int IsReachable =
61 m_IgnoreWalls ?
62 !Collision()->IntersectNoLaserNoWalls(Pos0: m_Pos, Pos1: pTarget->m_Pos, pOutCollision: nullptr, pOutBeforeCollision: nullptr) :
63 !Collision()->IntersectNoLaser(Pos0: m_Pos, Pos1: pTarget->m_Pos, pOutCollision: nullptr, pOutBeforeCollision: nullptr);
64 if(IsReachable)
65 {
66 const int &TargetClientId = pTarget->GetCid();
67 int Distance = distance(a: pTarget->m_Pos, b: m_Pos);
68 if(MinDistInTeam == 0 || MinDistInTeam > Distance)
69 {
70 MinDistInTeam = Distance;
71 ClosestTargetId = TargetClientId;
72 }
73 if(TargetClientId == m_TargetId)
74 {
75 CanStillBeTeamTarget = true;
76 }
77 }
78 }
79
80 // Set the closest player for each team as a target if the team does not have a target player yet
81 if((m_TargetId != -1 && !CanStillBeTeamTarget) || m_TargetId == -1)
82 {
83 m_TargetId = ClosestTargetId;
84 }
85}
86
87void CDragger::DraggerBeamReset()
88{
89 m_TargetId = -1;
90}
91
92void CDragger::DraggerBeamTick()
93{
94 CCharacter *pTarget = GameWorld()->GetCharacterById(Id: m_TargetId);
95 if(!pTarget)
96 {
97 DraggerBeamReset();
98 return;
99 }
100
101 if(GameWorld()->GameTick() % (int)(GameWorld()->GameTickSpeed() * 0.15f) == 0)
102 {
103 if(m_Layer == LAYER_SWITCH && m_Number > 0 &&
104 !Switchers()[m_Number].m_aStatus[pTarget->Team()])
105 {
106 DraggerBeamReset();
107 return;
108 }
109 }
110
111 // When the dragger can no longer reach the target player, the dragger beam dissolves
112 int IsReachable =
113 m_IgnoreWalls ?
114 !Collision()->IntersectNoLaserNoWalls(Pos0: m_Pos, Pos1: pTarget->m_Pos, pOutCollision: nullptr, pOutBeforeCollision: nullptr) :
115 !Collision()->IntersectNoLaser(Pos0: m_Pos, Pos1: pTarget->m_Pos, pOutCollision: nullptr, pOutBeforeCollision: nullptr);
116 if(!IsReachable || distance(a: pTarget->m_Pos, b: m_Pos) >= g_Config.m_SvDraggerRange)
117 {
118 DraggerBeamReset();
119 return;
120 }
121 // In the center of the dragger a tee does not experience speed-up
122 else if(distance(a: pTarget->m_Pos, b: m_Pos) > 28)
123 {
124 pTarget->AddVelocity(Addition: normalize(v: m_Pos - pTarget->m_Pos) * m_Strength);
125 }
126}
127
128CDragger::CDragger(CGameWorld *pGameWorld, int Id, const CLaserData *pData) :
129 CEntity(pGameWorld, CGameWorld::ENTTYPE_DRAGGER)
130{
131 m_Core = vec2(0.f, 0.f);
132 m_Id = Id;
133 m_TargetId = -1;
134
135 m_Strength = 0;
136 m_IgnoreWalls = false;
137 if(0 <= pData->m_Subtype && pData->m_Subtype < NUM_LASERDRAGGERTYPES)
138 {
139 m_IgnoreWalls = (pData->m_Subtype & 1);
140 m_Strength = (pData->m_Subtype >> 1) + 1;
141 }
142 m_Number = pData->m_SwitchNumber;
143 m_Layer = m_Number > 0 ? LAYER_SWITCH : LAYER_GAME;
144
145 Read(pData);
146}
147
148void CDragger::Read(const CLaserData *pData)
149{
150 m_Pos = pData->m_From;
151 m_TargetId = pData->m_Owner;
152}
153
154bool CDragger::Match(CDragger *pDragger)
155{
156 return pDragger->m_Strength == m_Strength && pDragger->m_Number == m_Number && pDragger->m_IgnoreWalls == m_IgnoreWalls;
157}
158