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 | |
12 | void 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 | |
30 | void 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 | |
90 | void CDragger::DraggerBeamReset() |
91 | { |
92 | m_TargetId = -1; |
93 | } |
94 | |
95 | void 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 | |
131 | CDragger::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 | |
151 | void CDragger::Read(const CLaserData *pData) |
152 | { |
153 | m_Pos = pData->m_From; |
154 | m_TargetId = pData->m_Owner; |
155 | } |
156 | |
157 | bool CDragger::Match(CDragger *pDragger) |
158 | { |
159 | return pDragger->m_Strength == m_Strength && pDragger->m_Number == m_Number && pDragger->m_IgnoreWalls == m_IgnoreWalls; |
160 | } |
161 | |