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