1 | /* See "licence DDRace.txt" and the readme.txt in the root of the distribution for more information. */ |
2 | #include "dragger_beam.h" |
3 | #include "character.h" |
4 | #include "dragger.h" |
5 | |
6 | #include <engine/server.h> |
7 | #include <engine/shared/config.h> |
8 | |
9 | #include <game/generated/protocol.h> |
10 | #include <game/mapitems.h> |
11 | |
12 | #include <game/server/gamecontext.h> |
13 | |
14 | CDraggerBeam::CDraggerBeam(CGameWorld *pGameWorld, CDragger *pDragger, vec2 Pos, float Strength, bool IgnoreWalls, |
15 | int ForClientId, int Layer, int Number) : |
16 | CEntity(pGameWorld, CGameWorld::ENTTYPE_LASER) |
17 | { |
18 | m_pDragger = pDragger; |
19 | m_Pos = Pos; |
20 | m_Strength = Strength; |
21 | m_IgnoreWalls = IgnoreWalls; |
22 | m_ForClientId = ForClientId; |
23 | m_Active = true; |
24 | m_Layer = Layer; |
25 | m_Number = Number; |
26 | m_EvalTick = Server()->Tick(); |
27 | |
28 | GameWorld()->InsertEntity(pEntity: this); |
29 | } |
30 | |
31 | void CDraggerBeam::Tick() |
32 | { |
33 | if(!m_Active) |
34 | { |
35 | return; |
36 | } |
37 | |
38 | // Drag only if the player is reachable and alive |
39 | CCharacter *pTarget = GameServer()->GetPlayerChar(ClientId: m_ForClientId); |
40 | if(!pTarget) |
41 | { |
42 | Reset(); |
43 | return; |
44 | } |
45 | |
46 | // The following checks are necessary, because the checks in CDragger::LookForPlayersToDrag only take place |
47 | // after CDraggerBeam::Tick and only every 150ms |
48 | // When the dragger is disabled for the target player's team, the dragger beam dissolves. The check if a dragger |
49 | // is disabled is only executed every 150ms, so the beam can stay activated up to 6 extra ticks |
50 | if(Server()->Tick() % (int)(Server()->TickSpeed() * 0.15f) == 0) |
51 | { |
52 | if(m_Layer == LAYER_SWITCH && m_Number > 0 && |
53 | !Switchers()[m_Number].m_aStatus[pTarget->Team()]) |
54 | { |
55 | Reset(); |
56 | return; |
57 | } |
58 | } |
59 | |
60 | // When the dragger can no longer reach the target player, the dragger beam dissolves |
61 | int IsReachable = |
62 | m_IgnoreWalls ? |
63 | !GameServer()->Collision()->IntersectNoLaserNW(Pos0: m_Pos, Pos1: pTarget->m_Pos, pOutCollision: 0, pOutBeforeCollision: 0) : |
64 | !GameServer()->Collision()->IntersectNoLaser(Pos0: m_Pos, Pos1: pTarget->m_Pos, pOutCollision: 0, pOutBeforeCollision: 0); |
65 | if(!IsReachable || |
66 | distance(a: pTarget->m_Pos, b: m_Pos) >= g_Config.m_SvDraggerRange || !pTarget->IsAlive()) |
67 | { |
68 | Reset(); |
69 | return; |
70 | } |
71 | // In the center of the dragger a tee does not experience speed-up |
72 | else if(distance(a: pTarget->m_Pos, b: m_Pos) > 28) |
73 | { |
74 | pTarget->AddVelocity(Addition: normalize(v: m_Pos - pTarget->m_Pos) * m_Strength); |
75 | } |
76 | } |
77 | |
78 | void CDraggerBeam::SetPos(vec2 Pos) |
79 | { |
80 | m_Pos = Pos; |
81 | } |
82 | |
83 | void CDraggerBeam::Reset() |
84 | { |
85 | m_MarkedForDestroy = true; |
86 | m_Active = false; |
87 | |
88 | m_pDragger->RemoveDraggerBeam(ClientId: m_ForClientId); |
89 | } |
90 | |
91 | void CDraggerBeam::Snap(int SnappingClient) |
92 | { |
93 | if(!m_Active) |
94 | { |
95 | return; |
96 | } |
97 | |
98 | // Only players who can see the player attached to the dragger can see the dragger beam |
99 | CCharacter *pTarget = GameServer()->GetPlayerChar(ClientId: m_ForClientId); |
100 | if(!pTarget || !pTarget->CanSnapCharacter(SnappingClient)) |
101 | { |
102 | return; |
103 | } |
104 | // Only players with the dragger beam in their field of view or who want to see everything will receive the snap |
105 | vec2 TargetPos = vec2(pTarget->m_Pos.x, pTarget->m_Pos.y); |
106 | if(distance(a: pTarget->m_Pos, b: m_Pos) >= g_Config.m_SvDraggerRange || NetworkClippedLine(SnappingClient, StartPos: m_Pos, EndPos: TargetPos)) |
107 | { |
108 | return; |
109 | } |
110 | |
111 | int Subtype = (m_IgnoreWalls ? 1 : 0) | (clamp(val: round_to_int(f: m_Strength - 1.f), lo: 0, hi: 2) << 1); |
112 | |
113 | int StartTick = m_EvalTick; |
114 | if(StartTick < Server()->Tick() - 4) |
115 | { |
116 | StartTick = Server()->Tick() - 4; |
117 | } |
118 | else if(StartTick > Server()->Tick()) |
119 | { |
120 | StartTick = Server()->Tick(); |
121 | } |
122 | |
123 | int SnappingClientVersion = GameServer()->GetClientVersion(ClientId: SnappingClient); |
124 | if(SnappingClientVersion >= VERSION_DDNET_ENTITY_NETOBJS) |
125 | { |
126 | StartTick = -1; |
127 | } |
128 | |
129 | int SnapObjId = GetId(); |
130 | if(m_pDragger->WillDraggerBeamUseDraggerId(TargetClientId: m_ForClientId, SnappingClientId: SnappingClient)) |
131 | { |
132 | SnapObjId = m_pDragger->GetId(); |
133 | } |
134 | |
135 | GameServer()->SnapLaserObject(Context: CSnapContext(SnappingClientVersion), SnapId: SnapObjId, |
136 | To: TargetPos, From: m_Pos, StartTick, Owner: m_ForClientId, LaserType: LASERTYPE_DRAGGER, Subtype, SwitchNumber: m_Number); |
137 | } |
138 | |
139 | void CDraggerBeam::SwapClients(int Client1, int Client2) |
140 | { |
141 | m_ForClientId = m_ForClientId == Client1 ? Client2 : m_ForClientId == Client2 ? Client1 : m_ForClientId; |
142 | } |
143 | |