1 | /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ |
2 | /* If you are missing that file, acquire a complete release at teeworlds.com. */ |
3 | #include <engine/graphics.h> |
4 | #include <engine/shared/config.h> |
5 | #include <engine/textrender.h> |
6 | |
7 | #include <game/generated/protocol.h> |
8 | |
9 | #include <game/client/gameclient.h> |
10 | #include <game/client/prediction/entities/character.h> |
11 | #include <game/localization.h> |
12 | |
13 | #include "debughud.h" |
14 | |
15 | static constexpr int64_t GRAPH_MAX_VALUES = 128; |
16 | |
17 | CDebugHud::CDebugHud() : |
18 | m_RampGraph(GRAPH_MAX_VALUES), |
19 | m_ZoomedInGraph(GRAPH_MAX_VALUES) |
20 | { |
21 | } |
22 | |
23 | void CDebugHud::RenderNetCorrections() |
24 | { |
25 | if(!g_Config.m_Debug || g_Config.m_DbgGraphs || !m_pClient->m_Snap.m_pLocalCharacter || !m_pClient->m_Snap.m_pLocalPrevCharacter) |
26 | return; |
27 | |
28 | const float Height = 300.0f; |
29 | const float Width = Height * Graphics()->ScreenAspect(); |
30 | Graphics()->MapScreen(TopLeftX: 0.0f, TopLeftY: 0.0f, BottomRightX: Width, BottomRightY: Height); |
31 | |
32 | const float Velspeed = length(a: vec2(m_pClient->m_Snap.m_pLocalCharacter->m_VelX / 256.0f, m_pClient->m_Snap.m_pLocalCharacter->m_VelY / 256.0f)) * Client()->GameTickSpeed(); |
33 | const float VelspeedX = m_pClient->m_Snap.m_pLocalCharacter->m_VelX / 256.0f * Client()->GameTickSpeed(); |
34 | const float VelspeedY = m_pClient->m_Snap.m_pLocalCharacter->m_VelY / 256.0f * Client()->GameTickSpeed(); |
35 | const float Ramp = VelocityRamp(Value: Velspeed, Start: m_pClient->m_aTuning[g_Config.m_ClDummy].m_VelrampStart, Range: m_pClient->m_aTuning[g_Config.m_ClDummy].m_VelrampRange, Curvature: m_pClient->m_aTuning[g_Config.m_ClDummy].m_VelrampCurvature); |
36 | const CCharacter *pCharacter = m_pClient->m_GameWorld.GetCharacterById(Id: m_pClient->m_Snap.m_LocalClientId); |
37 | |
38 | const float FontSize = 5.0f; |
39 | const float LineHeight = FontSize + 1.0f; |
40 | |
41 | float y = 50.0f; |
42 | char aBuf[128]; |
43 | const auto &&RenderRow = [&](const char *pLabel, const char *pValue) { |
44 | TextRender()->Text(x: Width - 100.0f, y, Size: FontSize, pText: pLabel); |
45 | TextRender()->Text(x: Width - 10.0f - TextRender()->TextWidth(Size: FontSize, pText: pValue), y, Size: FontSize, pText: pValue); |
46 | y += LineHeight; |
47 | }; |
48 | |
49 | TextRender()->TextColor(rgb: TextRender()->DefaultTextColor()); |
50 | |
51 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%.0f Bps" , Velspeed / 32); |
52 | RenderRow("Velspeed:" , aBuf); |
53 | |
54 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%.0f Bps" , VelspeedX / 32 * Ramp); |
55 | RenderRow("Velspeed.x * Ramp:" , aBuf); |
56 | |
57 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%.0f Bps" , VelspeedY / 32); |
58 | RenderRow("Velspeed.y:" , aBuf); |
59 | |
60 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%.2f" , Ramp); |
61 | RenderRow("Ramp:" , aBuf); |
62 | |
63 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%d" , pCharacter == nullptr ? -1 : pCharacter->m_TeleCheckpoint); |
64 | RenderRow("Checkpoint:" , aBuf); |
65 | |
66 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%d" , pCharacter == nullptr ? -1 : pCharacter->m_TuneZone); |
67 | RenderRow("Tune zone:" , aBuf); |
68 | |
69 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%.2f" , m_pClient->m_Snap.m_pLocalCharacter->m_X / 32.0f); |
70 | RenderRow("Pos.x:" , aBuf); |
71 | |
72 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%.2f" , m_pClient->m_Snap.m_pLocalCharacter->m_Y / 32.0f); |
73 | RenderRow("Pos.y:" , aBuf); |
74 | |
75 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%d" , m_pClient->m_Snap.m_pLocalCharacter->m_Angle); |
76 | RenderRow("Angle:" , aBuf); |
77 | |
78 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%d" , m_pClient->NetobjNumCorrections()); |
79 | RenderRow("Netobj corrections" , aBuf); |
80 | RenderRow(" on:" , m_pClient->NetobjCorrectedOn()); |
81 | } |
82 | |
83 | void CDebugHud::RenderTuning() |
84 | { |
85 | enum |
86 | { |
87 | DBG_TUNING_OFF = 0, |
88 | DBG_TUNING_SHOW_CHANGED, |
89 | DBG_TUNING_SHOW_ALL, |
90 | }; |
91 | |
92 | if(g_Config.m_DbgTuning == DBG_TUNING_OFF) |
93 | return; |
94 | |
95 | const CCharacter *pCharacter = m_pClient->m_GameWorld.GetCharacterById(Id: m_pClient->m_Snap.m_LocalClientId); |
96 | |
97 | const CTuningParams StandardTuning; |
98 | const CTuningParams *pGlobalTuning = m_pClient->GetTuning(i: 0); |
99 | const CTuningParams *pZoneTuning = !m_pClient->m_GameWorld.m_WorldConfig.m_UseTuneZones || pCharacter == nullptr ? nullptr : m_pClient->GetTuning(i: pCharacter->m_TuneZone); |
100 | const CTuningParams *pActiveTuning = pZoneTuning == nullptr ? pGlobalTuning : pZoneTuning; |
101 | |
102 | const float Height = 300.0f; |
103 | const float Width = Height * Graphics()->ScreenAspect(); |
104 | Graphics()->MapScreen(TopLeftX: 0.0f, TopLeftY: 0.0f, BottomRightX: Width, BottomRightY: Height); |
105 | |
106 | const float FontSize = 5.0f; |
107 | |
108 | const float StartY = 50.0f; |
109 | float y = StartY; |
110 | float StartX = 30.0f; |
111 | const auto &&RenderRow = [&](const char *pCol1, const char *pCol2, const char *pCol3) { |
112 | float x = StartX; |
113 | TextRender()->Text(x: x - TextRender()->TextWidth(Size: FontSize, pText: pCol1), y, Size: FontSize, pText: pCol1); |
114 | |
115 | x += 30.0f; |
116 | TextRender()->Text(x: x - TextRender()->TextWidth(Size: FontSize, pText: pCol2), y, Size: FontSize, pText: pCol2); |
117 | |
118 | x += 10.0f; |
119 | TextRender()->Text(x, y, Size: FontSize, pText: pCol3); |
120 | |
121 | y += FontSize + 1.0f; |
122 | |
123 | if(y >= Height - 80.0f) |
124 | { |
125 | y = StartY; |
126 | StartX += 130.0f; |
127 | } |
128 | }; |
129 | |
130 | for(int i = 0; i < CTuningParams::Num(); i++) |
131 | { |
132 | float CurrentGlobal, CurrentZone, Standard; |
133 | pGlobalTuning->Get(Index: i, pValue: &CurrentGlobal); |
134 | if(pZoneTuning == nullptr) |
135 | CurrentZone = 0.0f; |
136 | else |
137 | pZoneTuning->Get(Index: i, pValue: &CurrentZone); |
138 | StandardTuning.Get(Index: i, pValue: &Standard); |
139 | |
140 | if(g_Config.m_DbgTuning == DBG_TUNING_SHOW_CHANGED && Standard == CurrentGlobal && (pZoneTuning == nullptr || Standard == CurrentZone)) |
141 | continue; // skip unchanged params |
142 | |
143 | if(y == StartY) |
144 | { |
145 | TextRender()->TextColor(rgb: TextRender()->DefaultTextColor()); |
146 | RenderRow("Standard" , "Current" , "Tuning" ); |
147 | } |
148 | |
149 | ColorRGBA TextColor; |
150 | if(g_Config.m_DbgTuning == DBG_TUNING_SHOW_ALL && Standard == CurrentGlobal && (pZoneTuning == nullptr || Standard == CurrentZone)) |
151 | TextColor = ColorRGBA(0.75f, 0.75f, 0.75f, 1.0f); // grey: value unchanged globally and in current zone |
152 | else if(Standard == CurrentGlobal && pZoneTuning != nullptr && Standard != CurrentZone) |
153 | TextColor = ColorRGBA(0.6f, 0.6f, 1.0f, 1.0f); // blue: value changed only in current zone |
154 | else if(Standard != CurrentGlobal && pZoneTuning != nullptr && Standard == CurrentZone) |
155 | TextColor = ColorRGBA(0.4f, 1.0f, 0.4f, 1.0f); // green: value changed globally but reset to default by tune zone |
156 | else |
157 | TextColor = ColorRGBA(1.0f, 0.5f, 0.5f, 1.0f); // red: value changed globally |
158 | TextRender()->TextColor(rgb: TextColor); |
159 | |
160 | char aBufStandard[32]; |
161 | str_format(buffer: aBufStandard, buffer_size: sizeof(aBufStandard), format: "%.2f" , Standard); |
162 | char aBufCurrent[32]; |
163 | str_format(buffer: aBufCurrent, buffer_size: sizeof(aBufCurrent), format: "%.2f" , pZoneTuning == nullptr ? CurrentGlobal : CurrentZone); |
164 | RenderRow(aBufStandard, aBufCurrent, CTuningParams::Name(Index: i)); |
165 | } |
166 | |
167 | TextRender()->TextColor(rgb: TextRender()->DefaultTextColor()); |
168 | if(g_Config.m_DbgTuning == DBG_TUNING_SHOW_CHANGED) |
169 | return; |
170 | |
171 | // Render Velspeed.X * Ramp Graphs |
172 | Graphics()->MapScreen(TopLeftX: 0.0f, TopLeftY: 0.0f, BottomRightX: Graphics()->ScreenWidth(), BottomRightY: Graphics()->ScreenHeight()); |
173 | const float GraphSpacing = Graphics()->ScreenWidth() / 100.0f; |
174 | const float GraphW = Graphics()->ScreenWidth() / 4.0f; |
175 | const float GraphH = Graphics()->ScreenHeight() / 6.0f; |
176 | const float GraphX = GraphW; |
177 | const float GraphY = Graphics()->ScreenHeight() - GraphH - GraphSpacing; |
178 | |
179 | const int StepSizeRampGraph = 270; |
180 | const int StepSizeZoomedInGraph = 14; |
181 | if(m_OldVelrampStart != pActiveTuning->m_VelrampStart || m_OldVelrampRange != pActiveTuning->m_VelrampRange || m_OldVelrampCurvature != pActiveTuning->m_VelrampCurvature) |
182 | { |
183 | m_OldVelrampStart = pActiveTuning->m_VelrampStart; |
184 | m_OldVelrampRange = pActiveTuning->m_VelrampRange; |
185 | m_OldVelrampCurvature = pActiveTuning->m_VelrampCurvature; |
186 | |
187 | m_RampGraph.Init(Min: 0.0f, Max: 0.0f); |
188 | m_SpeedTurningPoint = 0; |
189 | float PreviousRampedSpeed = 1.0f; |
190 | for(int64_t i = 0; i < GRAPH_MAX_VALUES; i++) |
191 | { |
192 | // This is a calculation of the speed values per second on the X axis, from 270 to 34560 in steps of 270 |
193 | const float Speed = (i + 1) * StepSizeRampGraph; |
194 | const float Ramp = VelocityRamp(Value: Speed, Start: m_pClient->m_aTuning[g_Config.m_ClDummy].m_VelrampStart, Range: m_pClient->m_aTuning[g_Config.m_ClDummy].m_VelrampRange, Curvature: m_pClient->m_aTuning[g_Config.m_ClDummy].m_VelrampCurvature); |
195 | const float RampedSpeed = Speed * Ramp; |
196 | if(RampedSpeed >= PreviousRampedSpeed) |
197 | { |
198 | m_RampGraph.InsertAt(Time: i, Value: RampedSpeed / 32, Color: ColorRGBA(0.0f, 1.0f, 0.0f, 0.75f)); |
199 | m_SpeedTurningPoint = Speed; |
200 | } |
201 | else |
202 | { |
203 | m_RampGraph.InsertAt(Time: i, Value: RampedSpeed / 32, Color: ColorRGBA(1.0f, 0.0f, 0.0f, 0.75f)); |
204 | } |
205 | PreviousRampedSpeed = RampedSpeed; |
206 | } |
207 | m_RampGraph.Scale(WantedTotalTime: GRAPH_MAX_VALUES - 1); |
208 | |
209 | m_ZoomedInGraph.Init(Min: 0.0f, Max: 0.0f); |
210 | PreviousRampedSpeed = 1.0f; |
211 | MiddleOfZoomedInGraph = m_SpeedTurningPoint; |
212 | for(int64_t i = 0; i < GRAPH_MAX_VALUES; i++) |
213 | { |
214 | // This is a calculation of the speed values per second on the X axis, from (MiddleOfZoomedInGraph - 64 * StepSize) to (MiddleOfZoomedInGraph + 64 * StepSize) |
215 | const float Speed = MiddleOfZoomedInGraph - 64 * StepSizeZoomedInGraph + i * StepSizeZoomedInGraph; |
216 | const float Ramp = VelocityRamp(Value: Speed, Start: m_pClient->m_aTuning[g_Config.m_ClDummy].m_VelrampStart, Range: m_pClient->m_aTuning[g_Config.m_ClDummy].m_VelrampRange, Curvature: m_pClient->m_aTuning[g_Config.m_ClDummy].m_VelrampCurvature); |
217 | const float RampedSpeed = Speed * Ramp; |
218 | if(RampedSpeed >= PreviousRampedSpeed) |
219 | { |
220 | m_ZoomedInGraph.InsertAt(Time: i, Value: RampedSpeed / 32, Color: ColorRGBA(0.0f, 1.0f, 0.0f, 0.75f)); |
221 | m_SpeedTurningPoint = Speed; |
222 | } |
223 | else |
224 | { |
225 | m_ZoomedInGraph.InsertAt(Time: i, Value: RampedSpeed / 32, Color: ColorRGBA(1.0f, 0.0f, 0.0f, 0.75f)); |
226 | } |
227 | if(i == 0) |
228 | { |
229 | m_ZoomedInGraph.SetMin(RampedSpeed / 32); |
230 | } |
231 | PreviousRampedSpeed = RampedSpeed; |
232 | } |
233 | m_ZoomedInGraph.Scale(WantedTotalTime: GRAPH_MAX_VALUES - 1); |
234 | } |
235 | |
236 | const float GraphFontSize = 12.0f; |
237 | char aBuf[128]; |
238 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "Velspeed.X * Ramp in Bps (Velspeed %d to %d)" , StepSizeRampGraph / 32, 128 * StepSizeRampGraph / 32); |
239 | m_RampGraph.Render(pGraphics: Graphics(), pTextRender: TextRender(), x: GraphX, y: GraphY, w: GraphW, h: GraphH, pDescription: aBuf); |
240 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "Max Velspeed before it ramps off: %.2f Bps" , m_SpeedTurningPoint / 32); |
241 | TextRender()->Text(x: GraphX, y: GraphY - GraphFontSize, Size: GraphFontSize, pText: aBuf); |
242 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "Zoomed in on turning point (Velspeed %d to %d)" , ((int)MiddleOfZoomedInGraph - 64 * StepSizeZoomedInGraph) / 32, ((int)MiddleOfZoomedInGraph + 64 * StepSizeZoomedInGraph) / 32); |
243 | m_ZoomedInGraph.Render(pGraphics: Graphics(), pTextRender: TextRender(), x: GraphX + GraphW + GraphSpacing, y: GraphY, w: GraphW, h: GraphH, pDescription: aBuf); |
244 | } |
245 | |
246 | void CDebugHud::RenderHint() |
247 | { |
248 | if(!g_Config.m_Debug) |
249 | return; |
250 | |
251 | const float Height = 300.0f; |
252 | const float Width = Height * Graphics()->ScreenAspect(); |
253 | Graphics()->MapScreen(TopLeftX: 0.0f, TopLeftY: 0.0f, BottomRightX: Width, BottomRightY: Height); |
254 | |
255 | const float FontSize = 5.0f; |
256 | const float Spacing = 5.0f; |
257 | |
258 | TextRender()->TextColor(rgb: TextRender()->DefaultTextColor()); |
259 | TextRender()->Text(x: Spacing, y: Height - FontSize - Spacing, Size: FontSize, pText: Localize(pStr: "Debug mode enabled. Press Ctrl+Shift+D to disable debug mode." )); |
260 | } |
261 | |
262 | void CDebugHud::OnRender() |
263 | { |
264 | if(Client()->State() != IClient::STATE_ONLINE && Client()->State() != IClient::STATE_DEMOPLAYBACK) |
265 | return; |
266 | |
267 | RenderTuning(); |
268 | RenderNetCorrections(); |
269 | RenderHint(); |
270 | } |
271 | |