script, ui: added "sv_min_startmap_health" cvar
[k8vavoom.git] / source / touch.cpp
blob598897555d354f010757a6c6c73f3edae26beb69
1 //**************************************************************************
2 //**
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
9 //**
10 //** Copyright (C) 1999-2006 Jānis Legzdiņš
11 //** Copyright (C) 2018-2023 Ketmar Dark
12 //**
13 //** This program is free software: you can redistribute it and/or modify
14 //** it under the terms of the GNU General Public License as published by
15 //** the Free Software Foundation, version 3 of the License ONLY.
16 //**
17 //** This program is distributed in the hope that it will be useful,
18 //** but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 //** GNU General Public License for more details.
21 //**
22 //** You should have received a copy of the GNU General Public License
23 //** along with this program. If not, see <http://www.gnu.org/licenses/>.
24 //**
25 //**************************************************************************
26 #if defined(ANDROID) || 0
27 #ifdef VAVOOM_CUSTOM_SPECIAL_SDL
28 # include <SDL.h>
29 #else
30 # include <SDL2/SDL.h>
31 #endif
32 #include "gamedefs.h"
33 #include "touch.h"
34 #include "drawer.h"
35 #include "menu.h"
36 #include "input.h"
37 #include "touch.h"
39 #define ID_UNDEF (-666)
41 #define X_ROT_SENSITIVITY (1.0f)
42 #define Y_ROT_SENSITIVITY (1.0f)
43 #define X_ROT_THRESHOLD (0.01f)
44 #define Y_ROT_THRESHOLD (0.01f)
46 #define X_STEP_THRESHOLD (0.05f)
47 #define Y_STEP_THRESHOLD (0.05f)
49 #define STEP_FINGER (0)
50 #define ROT_FINGER (1)
51 #define MAX_FINGERS (2)
54 static VCvarB touch_enable("touch_enable", true, "Show and handle touch screen controls (when available).", CVAR_Archive|CVAR_NoShadow);
57 static struct vfinger {
58 int id; // system finger id
59 float x, y; // [0..1]
60 float dx, dy; // [-1..+1]
61 float start_x, start_y; // [0..1]
62 float vx, vy; // [-1..+1]
63 } fingers[MAX_FINGERS] = {
64 { ID_UNDEF, 0.0f, 0.0f },
65 { ID_UNDEF, 0.0f, 0.0f },
68 #define MAX_BUTTONS (9)
70 static struct vbutton {
71 float x, y, r;
72 const char *press;
73 const char *release;
74 int key;
75 int finger;
76 } buttons[MAX_BUTTONS] = {
77 { 0.85f, 0.5f, 0.08f, "+Attack", "-Attack", K_ENTER, ID_UNDEF },
78 { 1.0f, 1.0f, 0.1f, "+Use", "-Use", 0, ID_UNDEF },
79 { 0.8f, 1.0f, 0.1f, "+Crouch", "-Crouch", 0, ID_UNDEF },
80 { 0.6f, 1.0f, 0.1f, "+Jump", "-Jump", 0, ID_UNDEF },
81 { 0.6f, 0.0f, 0.1f, "impulse 17", "", 0, ID_UNDEF }, // prev weapon
82 { 0.8f, 0.0f, 0.1f, "impulse 18", "", 0, ID_UNDEF }, // next weapon
83 { 1.0f, 0.0f, 0.1f, "ToggleAlwaysRun", "", 0, ID_UNDEF },
84 { 0.0f, 1.0f, 0.1f, "SetMenu Main", "", K_ESCAPE, ID_UNDEF },
85 { 0.0f, 0.0f, 0.1f, "ToggleConsole", "", 0, ID_UNDEF },
89 //==========================================================================
91 // fsgn
93 // k8: this can be made faster, but who cares?
95 //==========================================================================
96 static inline float fsgn (const float x) noexcept {
97 return (x < 0.0f ? -1.0f : x > 0.0f ? +1.0f : 0.0f);
101 //==========================================================================
103 // point_in_circle
105 //==========================================================================
106 static inline bool point_in_circle (const float x, const float y, const float cx, const float cy, const float cr) noexcept {
107 //return (powf(x-cx, 2)+powf(y-cy, 2) < powf(cr, 2));
108 // no, don't use `powf()`, you silly horse! it's freakin' slow.
109 const float dx = x-cx;
110 const float dy = y-cy;
111 return (dx*dx+dy*dy < cr*cr);
115 //==========================================================================
117 // draw_pad
119 // TODO: make colors and opacities configurable via cvars
121 //==========================================================================
122 static void draw_pad (int f, float x_threshold, float y_threshold) {
123 if (fingers[f].id != ID_UNDEF) {
124 const int scr_w = Drawer->getWidth();
125 const int scr_h = Drawer->getHeight();
126 const float sx = fingers[f].start_x*scr_w;
127 const float sy = fingers[f].start_y*scr_h;
128 float tx = x_threshold*scr_w;
129 float ty = y_threshold*scr_h;
130 Drawer->DrawRect(sx-tx, sy-ty, sx+tx, sy+ty, 0xff8f0f00, 1.0f);
131 tx /= 4.0f;
132 ty /= 4.0f;
133 const float x = fingers[f].x*scr_w;
134 const float y = fingers[f].y*scr_h;
135 Drawer->FillRect(sx-tx, sy-ty, sx+tx, sy+ty, 0xffff0f00, 0.5f);
136 Drawer->FillRect(x-tx, y-ty, x+tx, y+ty, 0xffff0f3f, 0.5f);
141 //==========================================================================
143 // Touch_Draw
145 // TODO: make colors and opacities configurable via cvars
147 //==========================================================================
148 void Touch_Draw () {
149 if (!touch_enable || SDL_GetNumTouchDevices() <= 0 || SDL_IsTextInputActive()) {
150 return;
153 const int scr_w = Drawer->getWidth();
154 const int scr_h = Drawer->getHeight();
155 for (int i = 0; i < MAX_BUTTONS; ++i) {
156 const int x = buttons[i].x*scr_w;
157 const int y = buttons[i].y*scr_h;
158 const int tx = buttons[i].r*scr_w;
159 const int ty = buttons[i].r*scr_h;
160 Drawer->DrawRect(x-tx, y-ty, x+tx, y+ty, 0xff00ff00, 0.8f);
163 draw_pad(STEP_FINGER, X_STEP_THRESHOLD, Y_STEP_THRESHOLD);
164 draw_pad(ROT_FINGER, X_ROT_THRESHOLD, Y_ROT_THRESHOLD);
168 //==========================================================================
170 // press_pad
172 //==========================================================================
173 static void press_pad (int key, bool down) {
174 if (MN_Active()) {
175 GInput->PostKeyEvent(key, down ? 1 : 0, 0);
176 } else {
177 const char *c = (down ? "+" : "-");
178 //if (pad == STEP_FINGER) {
179 switch (key) {
180 case K_UPARROW: GCmdBuf << c << "Forward\n"; break;
181 case K_DOWNARROW: GCmdBuf << c << "Backward\n"; break;
182 case K_LEFTARROW: GCmdBuf << c << "MoveLeft\n"; break;
183 case K_RIGHTARROW: GCmdBuf << c << "MoveRight\n"; break;
185 //} else if (pad == ROT_FINGER) {
186 // switch (key) {
187 // case K_UPARROW: GCmdBuf << c << "LookUp\n"; break;
188 // case K_DOWNARROW: GCmdBuf << c << "LookDown\n"; break;
189 // case K_LEFTARROW: GCmdBuf << c << "LookLeft\n"; break;
190 // case K_RIGHTARROW: GCmdBuf << c << "LookRight\n"; break;
191 // }
197 //==========================================================================
199 // PostMouseEvent
201 //==========================================================================
202 static void PostMouseEvent (const float dx, const float dy) {
203 if (!MN_Active()) {
204 event_t ev;
205 ev.clear();
206 ev.type = ev_mouse;
207 ev.modflags = 0;
208 ev.dx = X_ROT_SENSITIVITY*Drawer->getWidth()*dx;
209 ev.dy = Y_ROT_SENSITIVITY*Drawer->getHeight()*(-dy);
210 VObject::PostEvent(ev);
215 //==========================================================================
217 // Touch_Update
219 //==========================================================================
220 void Touch_Update () {
221 if (!touch_enable || SDL_GetNumTouchDevices() <= 0 || SDL_IsTextInputActive()) {
222 return;
225 if (fingers[ROT_FINGER].id != ID_UNDEF) {
226 float dx = fingers[ROT_FINGER].x-fingers[ROT_FINGER].start_x;
227 float dy = fingers[ROT_FINGER].y-fingers[ROT_FINGER].start_y;
228 dx = (fabsf(dx) >= X_ROT_THRESHOLD ? dx-fsgn(dx)*X_ROT_THRESHOLD : 0.0f);
229 dy = (fabsf(dy) >= X_ROT_THRESHOLD ? dy-fsgn(dy)*X_ROT_THRESHOLD : 0.0f);
230 if (dx != 0.0f || dy != 0.0f) {
231 PostMouseEvent(dx, dy);
235 if (fingers[STEP_FINGER].id != ID_UNDEF) {
236 float vx = fingers[STEP_FINGER].x-fingers[STEP_FINGER].start_x;
237 float vy = fingers[STEP_FINGER].y-fingers[STEP_FINGER].start_y;
238 vx = (fabsf(vx) >= X_STEP_THRESHOLD ? vx-fsgn(vx)*X_STEP_THRESHOLD : 0.0f);
239 vy = (fabsf(vy) >= X_STEP_THRESHOLD ? vy-fsgn(vy)*X_STEP_THRESHOLD : 0.0f);
240 vx = fsgn(vx);
241 vy = fsgn(vy);
242 if (vx != fingers[STEP_FINGER].vx) {
243 if (fingers[STEP_FINGER].vx < 0.0f) press_pad(K_LEFTARROW, false);
244 else if (fingers[STEP_FINGER].vx > 0.0f) press_pad(K_RIGHTARROW, false);
245 if (vx < 0.0f) press_pad(K_LEFTARROW, true);
246 else if (vx > 0.0f) press_pad(K_RIGHTARROW, true);
248 if (vy != fingers[STEP_FINGER].vy) {
249 if (fingers[STEP_FINGER].vy < 0.0f) press_pad(K_UPARROW, false);
250 else if (fingers[STEP_FINGER].vy > 0.0f) press_pad(K_DOWNARROW, false);
251 if (vy < 0.0f) press_pad(K_UPARROW, true);
252 else if (vy > 0.0f) press_pad(K_DOWNARROW, true);
254 fingers[STEP_FINGER].vx = vx;
255 fingers[STEP_FINGER].vy = vy;
260 //==========================================================================
262 // find_finger
264 //==========================================================================
265 static int find_finger (int id) noexcept {
266 for (unsigned i = 0; i < MAX_FINGERS; ++i) if (fingers[i].id == id) return (int)i;
267 return -1;
271 //==========================================================================
273 // press_button
275 //==========================================================================
276 static void press_button (int finger, float x, float y, bool down) {
277 for (unsigned i = 0; i < MAX_BUTTONS; ++i) {
278 if (!point_in_circle(x, y, buttons[i].x, buttons[i].y, buttons[i].r)) continue;
279 if (down) {
280 if (buttons[i].finger == ID_UNDEF) {
281 if (MN_Active()) {
282 if (buttons[i].key != 0) {
283 GInput->PostKeyEvent(buttons[i].key, 1, 0);
285 } else {
286 GCmdBuf << buttons[i].press << "\n";
288 buttons[i].finger = finger;
290 } else {
291 if (buttons[i].finger == finger) {
292 if (MN_Active()) {
293 if (buttons[i].key != 0) {
294 GInput->PostKeyEvent(buttons[i].key, 0, 0);
296 } else {
297 GCmdBuf << buttons[i].release << "\n";
299 buttons[i].finger = ID_UNDEF;
306 //==========================================================================
308 // Touch_Event
310 // x, y IN [0.0..1.0]
311 // dx, dy IN [-1.0..1.0]
312 // pressure IN [0.0..1.0]
314 //==========================================================================
315 void Touch_Event (int type, int id, float x, float y, float dx, float dy, float pressure) {
316 if (!touch_enable || SDL_GetNumTouchDevices() <= 0 || SDL_IsTextInputActive()) {
317 return;
320 int f;
321 switch (type) {
322 case TouchDown:
323 press_button(id, x, y, true);
324 f = (x < 0.5f ? STEP_FINGER : ROT_FINGER);
325 if (fingers[f].id == ID_UNDEF) {
326 fingers[f].id = id;
327 fingers[f].x = x;
328 fingers[f].y = y;
329 fingers[f].dx = dx;
330 fingers[f].dy = dy;
331 fingers[f].start_x = x;
332 fingers[f].start_y = y;
334 break;
335 case TouchMotion:
336 f = find_finger(id);
337 if (f >= 0) {
338 fingers[f].x = x;
339 fingers[f].y = y;
340 fingers[f].dx = dx;
341 fingers[f].dy = dy;
343 break;
344 case TouchUp:
345 press_button(id, x, y, false);
346 f = find_finger(id);
347 if (f >= 0) {
348 press_button(id, fingers[f].start_x, fingers[f].start_y, false);
349 fingers[f].id = ID_UNDEF;
350 if (f == STEP_FINGER) {
351 if (fingers[STEP_FINGER].vx < 0.0f) press_pad(K_LEFTARROW, false);
352 else if (fingers[STEP_FINGER].vx > 0.0f) press_pad(K_RIGHTARROW, false);
353 if (fingers[STEP_FINGER].vy < 0.0f) press_pad(K_UPARROW, false);
354 else if (fingers[STEP_FINGER].vy > 0.0f) press_pad(K_DOWNARROW, false);
355 } else if (f == ROT_FINGER) {
356 // nothing
359 break;
362 #endif