|
1 (* |
|
2 * Hedgewars, a free turn based strategy game |
|
3 * Copyright (c) 2011 Richard Deurwaarder <xeli@xelification.com> |
|
4 * |
|
5 * This program is free software; you can redistribute it and/or modify |
|
6 * it under the terms of the GNU General Public License as published by |
|
7 * the Free Software Foundation; version 2 of the License |
|
8 * |
|
9 * This program is distributed in the hope that it will be useful, |
|
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 * GNU General Public License for more details. |
|
13 * |
|
14 * You should have received a copy of the GNU General Public License |
|
15 * along with this program; if not, write to the Free Software |
|
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA |
|
17 *) |
|
18 |
|
19 {$INCLUDE "options.inc"} |
|
20 |
|
21 unit uTouch; |
|
22 |
|
23 interface |
|
24 |
|
25 uses sysutils, math, uConsole, uVariables, SDLh, uTypes, uFloat, uConsts, uIO, uCommands, GLUnit; |
|
26 |
|
27 // TODO: this type should be Int64 |
|
28 // TODO: this type should be named TSDL_FingerId |
|
29 type SDL_FingerId = LongInt; |
|
30 |
|
31 type |
|
32 PTouch_Finger = ^Touch_Finger; |
|
33 Touch_Finger = record |
|
34 id : SDL_FingerId; |
|
35 x,y : LongInt; |
|
36 historicalX, historicalY : LongInt; |
|
37 timeSinceDown : Longword; |
|
38 end; |
|
39 |
|
40 procedure initModule; |
|
41 |
|
42 procedure ProcessTouch; |
|
43 procedure onTouchDown(x,y: Longword; pointerId: SDL_FingerId); |
|
44 procedure onTouchMotion(x,y: Longword; dx,dy: LongInt; pointerId: SDL_FingerId); |
|
45 procedure onTouchUp(x,y: Longword; pointerId: SDL_FingerId); |
|
46 function convertToCursor(scale: LongInt; xy: LongInt): LongInt; |
|
47 function addFinger(x,y: Longword; id: SDL_FingerId): PTouch_Finger; |
|
48 procedure deleteFinger(id: SDL_FingerId); |
|
49 procedure onTouchClick(finger: Touch_Finger); |
|
50 procedure onTouchDoubleClick(finger: Touch_Finger); |
|
51 |
|
52 function findFinger(id: SDL_FingerId): PTouch_Finger; |
|
53 procedure aim(finger: Touch_Finger); |
|
54 function isOnCrosshair(finger: Touch_Finger): boolean; |
|
55 function isOnCurrentHog(finger: Touch_Finger): boolean; |
|
56 function isOnFireButton(finger: Touch_Finger): boolean; |
|
57 procedure convertToWorldCoord(var x,y: hwFloat; finger: Touch_Finger); |
|
58 procedure convertToFingerCoord(var x,y: hwFloat; oldX, oldY: hwFloat); |
|
59 function fingerHasMoved(finger: Touch_Finger): boolean; |
|
60 function calculateDelta(finger1, finger2: Touch_Finger): hwFloat; |
|
61 function getSecondFinger(finger: Touch_Finger): PTouch_Finger; |
|
62 procedure printFinger(finger: Touch_Finger); |
|
63 implementation |
|
64 |
|
65 const |
|
66 clicktime = 200; |
|
67 nilFingerId = High(SDL_FingerId); |
|
68 |
|
69 var |
|
70 fireButtonLeft, fireButtonRight, fireButtonTop, fireButtonBottom : LongInt; |
|
71 |
|
72 |
|
73 |
|
74 leftButtonBoundary : LongInt; |
|
75 rightButtonBoundary : LongInt; |
|
76 topButtonBoundary : LongInt; |
|
77 |
|
78 pointerCount : Longword; |
|
79 fingers: array of Touch_Finger; |
|
80 moveCursor : boolean; |
|
81 invertCursor : boolean; |
|
82 |
|
83 xTouchClick,yTouchClick : LongInt; |
|
84 timeSinceClick : Longword; |
|
85 |
|
86 //Pinch to zoom |
|
87 pinchSize : hwFloat; |
|
88 baseZoomValue: GLFloat; |
|
89 |
|
90 |
|
91 //aiming |
|
92 aiming, movingCrosshair: boolean; |
|
93 crosshairCommand: ShortString; |
|
94 targetAngle: LongInt; |
|
95 stopFiring: boolean; |
|
96 |
|
97 //moving |
|
98 stopLeft, stopRight, walkingLeft, walkingRight : boolean; |
|
99 |
|
100 |
|
101 procedure onTouchDown(x,y: Longword; pointerId: SDL_FingerId); |
|
102 var |
|
103 finger: PTouch_Finger; |
|
104 begin |
|
105 finger := addFinger(x,y,pointerId); |
|
106 case pointerCount of |
|
107 1: |
|
108 begin |
|
109 moveCursor:= false; |
|
110 if bShowAmmoMenu then |
|
111 begin |
|
112 moveCursor := true; |
|
113 exit; |
|
114 end; |
|
115 |
|
116 if isOnCrosshair(finger^) then |
|
117 begin |
|
118 aiming:= true; |
|
119 exit; |
|
120 end; |
|
121 |
|
122 if isOnFireButton(finger^) then |
|
123 begin |
|
124 stopFiring:= false; |
|
125 ParseCommand('+attack', true); |
|
126 exit; |
|
127 end; |
|
128 if (finger^.x < leftButtonBoundary) and (finger^.y < 390) then |
|
129 begin |
|
130 ParseCommand('+left', true); |
|
131 walkingLeft := true; |
|
132 exit; |
|
133 end; |
|
134 if finger^.x > rightButtonBoundary then |
|
135 begin |
|
136 ParseCommand('+right', true); |
|
137 walkingRight:= true; |
|
138 exit; |
|
139 end; |
|
140 if finger^.y < topButtonBoundary then |
|
141 begin |
|
142 ParseCommand('hjump', true); |
|
143 exit; |
|
144 end; |
|
145 moveCursor:= true; |
|
146 end; |
|
147 2: |
|
148 begin |
|
149 aiming:= false; |
|
150 stopFiring:= true; |
|
151 moveCursor:= false; |
|
152 pinchSize := calculateDelta(finger^, getSecondFinger(finger^)^); |
|
153 baseZoomValue := ZoomValue |
|
154 end; |
|
155 end;//end case pointerCount of |
|
156 end; |
|
157 |
|
158 procedure onTouchMotion(x,y: Longword;dx,dy: LongInt; pointerId: SDL_FingerId); |
|
159 var |
|
160 finger, secondFinger: PTouch_Finger; |
|
161 currentPinchDelta, zoom : hwFloat; |
|
162 tmpX, tmpY: LongInt; |
|
163 begin |
|
164 x := x; |
|
165 y := y; |
|
166 dx := dx; |
|
167 dy := dy; |
|
168 finger:= findFinger(pointerId); |
|
169 tmpX := convertToCursor(cScreenWidth, x); |
|
170 tmpY := convertToCursor(cScreenHeight, y); |
|
171 |
|
172 if moveCursor then |
|
173 begin |
|
174 if invertCursor then |
|
175 begin |
|
176 CursorPoint.X := CursorPoint.X + (finger^.x - tmpX); |
|
177 CursorPoint.Y := CursorPoint.Y - (finger^.y - tmpY); |
|
178 end |
|
179 else |
|
180 begin |
|
181 CursorPoint.X := CursorPoint.X - (finger^.x - tmpX); |
|
182 CursorPoint.Y := CursorPoint.Y + (finger^.y - tmpY); |
|
183 end; |
|
184 finger^.x := tmpX; |
|
185 finger^.y := tmpY; |
|
186 exit //todo change into switch rather than ugly ifs |
|
187 end; |
|
188 |
|
189 finger^.x := tmpX; |
|
190 finger^.y := tmpY; |
|
191 |
|
192 if aiming then |
|
193 begin |
|
194 aim(finger^); |
|
195 exit |
|
196 end; |
|
197 if pointerCount = 2 then |
|
198 begin |
|
199 secondFinger := getSecondFinger(finger^); |
|
200 currentPinchDelta := calculateDelta(finger^, secondFinger^) - pinchSize; |
|
201 zoom := currentPinchDelta/cScreenWidth; |
|
202 ZoomValue := baseZoomValue - ((hwFloat2Float(zoom) * cMinMaxZoomLevelDelta)); |
|
203 if ZoomValue < cMaxZoomLevel then ZoomValue := cMaxZoomLevel; |
|
204 if ZoomValue > cMinZoomLevel then ZoomValue := cMinZoomLevel; |
|
205 end; |
|
206 end; |
|
207 |
|
208 procedure onTouchUp(x,y: Longword; pointerId: SDL_FingerId); |
|
209 begin |
|
210 x := x; |
|
211 y := y; |
|
212 aiming:= false; |
|
213 stopFiring:= true; |
|
214 deleteFinger(pointerId); |
|
215 |
|
216 if walkingLeft then |
|
217 begin |
|
218 ParseCommand('-left', true); |
|
219 walkingLeft := false; |
|
220 end; |
|
221 |
|
222 if walkingRight then |
|
223 begin |
|
224 ParseCommand('-right', true); |
|
225 walkingRight := false; |
|
226 end; |
|
227 end; |
|
228 |
|
229 procedure onTouchDoubleClick(finger: Touch_Finger); |
|
230 begin |
|
231 finger := finger;//avoid compiler hint |
|
232 ParseCommand('ljump', true); |
|
233 end; |
|
234 |
|
235 procedure onTouchClick(finger: Touch_Finger); |
|
236 begin |
|
237 if (SDL_GetTicks - timeSinceClick < 300) and (DistanceI(finger.X-xTouchClick, finger.Y-yTouchClick) < _30) then |
|
238 begin |
|
239 onTouchDoubleClick(finger); |
|
240 exit; |
|
241 end |
|
242 else |
|
243 begin |
|
244 xTouchClick := finger.x; |
|
245 yTouchClick := finger.y; |
|
246 timeSinceClick := SDL_GetTicks; |
|
247 end; |
|
248 |
|
249 if bShowAmmoMenu then |
|
250 begin |
|
251 doPut(CursorPoint.X, CursorPoint.Y, false); |
|
252 exit |
|
253 end; |
|
254 |
|
255 if isOnCurrentHog(finger) then |
|
256 begin |
|
257 bShowAmmoMenu := true; |
|
258 exit; |
|
259 end; |
|
260 |
|
261 if finger.y < topButtonBoundary then |
|
262 begin |
|
263 ParseCommand('hjump', true); |
|
264 exit; |
|
265 end; |
|
266 end; |
|
267 |
|
268 function addFinger(x,y: Longword; id: SDL_FingerId): PTouch_Finger; |
|
269 var |
|
270 xCursor, yCursor, index : LongInt; |
|
271 begin |
|
272 //Check array sizes |
|
273 if length(fingers) < Integer(pointerCount) then |
|
274 begin |
|
275 setLength(fingers, length(fingers)*2); |
|
276 for index := length(fingers) div 2 to length(fingers) do fingers[index].id := nilFingerId; |
|
277 end; |
|
278 |
|
279 |
|
280 xCursor := convertToCursor(cScreenWidth, x); |
|
281 yCursor := convertToCursor(cScreenHeight, y); |
|
282 |
|
283 //on removing fingers, all fingers are moved to the left |
|
284 //with dynamic arrays being zero based, the new position of the finger is the old pointerCount |
|
285 fingers[pointerCount].id := id; |
|
286 fingers[pointerCount].historicalX := xCursor; |
|
287 fingers[pointerCount].historicalY := yCursor; |
|
288 fingers[pointerCount].x := xCursor; |
|
289 fingers[pointerCount].y := yCursor; |
|
290 fingers[pointerCount].timeSinceDown:= SDL_GetTicks; |
|
291 |
|
292 addFinger:= @fingers[pointerCount]; |
|
293 inc(pointerCount); |
|
294 end; |
|
295 |
|
296 procedure deleteFinger(id: SDL_FingerId); |
|
297 var |
|
298 index : Longword; |
|
299 begin |
|
300 |
|
301 dec(pointerCount); |
|
302 for index := 0 to pointerCount do |
|
303 begin |
|
304 if fingers[index].id = id then |
|
305 begin |
|
306 //Check for onTouchClick event |
|
307 if ((SDL_GetTicks - fingers[index].timeSinceDown) < clickTime) AND |
|
308 not(fingerHasMoved(fingers[index])) then onTouchClick(fingers[index]); |
|
309 |
|
310 //put the last finger into the spot of the finger to be removed, |
|
311 //so that all fingers are packed to the far left |
|
312 if pointerCount <> index then |
|
313 begin |
|
314 fingers[index].id := fingers[pointerCount].id; |
|
315 fingers[index].x := fingers[pointerCount].x; |
|
316 fingers[index].y := fingers[pointerCount].y; |
|
317 fingers[index].historicalX := fingers[pointerCount].historicalX; |
|
318 fingers[index].historicalY := fingers[pointerCount].historicalY; |
|
319 fingers[index].timeSinceDown := fingers[pointerCount].timeSinceDown; |
|
320 |
|
321 fingers[pointerCount].id := nilFingerId; |
|
322 end |
|
323 else fingers[index].id := nilFingerId; |
|
324 break; |
|
325 end; |
|
326 end; |
|
327 |
|
328 end; |
|
329 |
|
330 procedure ProcessTouch; |
|
331 var |
|
332 deltaAngle: LongInt; |
|
333 begin |
|
334 invertCursor := not(bShowAmmoMenu); |
|
335 if aiming then |
|
336 begin |
|
337 if CurrentHedgehog^.Gear <> nil then |
|
338 begin |
|
339 deltaAngle:= CurrentHedgehog^.Gear^.Angle - targetAngle; |
|
340 if (deltaAngle <> 0) and not(movingCrosshair) then |
|
341 begin |
|
342 ParseCommand('+' + crosshairCommand, true); |
|
343 movingCrosshair := true; |
|
344 end |
|
345 else |
|
346 if movingCrosshair then |
|
347 begin |
|
348 ParseCommand('-' + crosshairCommand, true); |
|
349 movingCrosshair:= false; |
|
350 end; |
|
351 end; |
|
352 end |
|
353 else if movingCrosshair then |
|
354 begin |
|
355 ParseCommand('-' + crosshairCommand, true); |
|
356 movingCrosshair := false; |
|
357 end; |
|
358 |
|
359 if stopFiring then |
|
360 begin |
|
361 ParseCommand('-attack', true); |
|
362 stopFiring:= false; |
|
363 end; |
|
364 |
|
365 if stopRight then |
|
366 begin |
|
367 stopRight := false; |
|
368 ParseCommand('-right', true); |
|
369 end; |
|
370 |
|
371 if stopLeft then |
|
372 begin |
|
373 stopLeft := false; |
|
374 ParseCommand('-left', true); |
|
375 end; |
|
376 |
|
377 end; |
|
378 |
|
379 function findFinger(id: SDL_FingerId): PTouch_Finger; |
|
380 var |
|
381 index: LongWord; |
|
382 begin |
|
383 for index := 0 to High(fingers) do |
|
384 if fingers[index].id = id then |
|
385 begin |
|
386 findFinger := @fingers[index]; |
|
387 break; |
|
388 end; |
|
389 end; |
|
390 |
|
391 procedure aim(finger: Touch_Finger); |
|
392 var |
|
393 hogX, hogY, touchX, touchY, deltaX, deltaY, tmpAngle: hwFloat; |
|
394 tmp: ShortString; |
|
395 begin |
|
396 if CurrentHedgehog^.Gear <> nil then |
|
397 begin |
|
398 touchX := _0;//avoid compiler hint |
|
399 touchY := _0; |
|
400 hogX := CurrentHedgehog^.Gear^.X; |
|
401 hogY := CurrentHedgehog^.Gear^.Y; |
|
402 |
|
403 convertToWorldCoord(touchX, touchY, finger); |
|
404 deltaX := hwAbs(TouchX-HogX); |
|
405 deltaY := (TouchY-HogY); |
|
406 |
|
407 tmpAngle:= DeltaY / Distance(deltaX, deltaY) *_2048; |
|
408 targetAngle:= (hwRound(tmpAngle) + 2048) div 2; |
|
409 |
|
410 tmp := crosshairCommand; |
|
411 if CurrentHedgehog^.Gear^.Angle - targetAngle < 0 then crosshairCommand := 'down' |
|
412 else crosshairCommand:= 'up'; |
|
413 if movingCrosshair and (tmp <> crosshairCommand) then |
|
414 begin |
|
415 ParseCommand('-' + tmp, true); |
|
416 movingCrosshair := false; |
|
417 end; |
|
418 |
|
419 end; //if CurrentHedgehog^.Gear <> nil |
|
420 end; |
|
421 |
|
422 function convertToCursor(scale: LongInt; xy: LongInt): LongInt; |
|
423 begin |
|
424 convertToCursor := round(xy/32768*scale) |
|
425 end; |
|
426 |
|
427 function isOnFireButton(finger: Touch_Finger): boolean; |
|
428 begin |
|
429 isOnFireButton:= (finger.x <= fireButtonRight) and (finger.x >= fireButtonLeft) and (finger.y <= fireButtonBottom) and (finger.y >= fireButtonTop); |
|
430 end; |
|
431 |
|
432 function isOnCrosshair(finger: Touch_Finger): boolean; |
|
433 var |
|
434 x,y : hwFloat; |
|
435 begin |
|
436 x := _0;//avoid compiler hint |
|
437 y := _0; |
|
438 convertToFingerCoord(x, y, int2hwFloat(CrosshairX), int2hwFloat(CrosshairY)); |
|
439 isOnCrosshair:= Distance(int2hwFloat(finger.x)-x, int2hwFloat(finger.y)-y) < _50; |
|
440 end; |
|
441 |
|
442 function isOnCurrentHog(finger: Touch_Finger): boolean; |
|
443 var |
|
444 x,y : hwFloat; |
|
445 begin |
|
446 x := _0; |
|
447 y := _0; |
|
448 convertToFingerCoord(x, y, CurrentHedgehog^.Gear^.X, CurrentHedgehog^.Gear^.Y); |
|
449 isOnCurrentHog := Distance(int2hwFloat(finger.X)-x, int2hwFloat(finger.Y)-y) < _50; |
|
450 end; |
|
451 |
|
452 procedure convertToFingerCoord(var x,y : hwFloat; oldX, oldY: hwFloat); |
|
453 begin |
|
454 x := oldX + int2hwFloat(WorldDx + (cScreenWidth div 2)); |
|
455 y := oldY + int2hwFloat(WorldDy); |
|
456 end; |
|
457 |
|
458 procedure convertToWorldCoord(var x,y: hwFloat; finger: Touch_Finger); |
|
459 begin |
|
460 //if x <> nil then |
|
461 x := int2hwFloat((finger.x-WorldDx) - (cScreenWidth div 2)); |
|
462 //if y <> nil then |
|
463 y := int2hwFloat(finger.y-WorldDy); |
|
464 end; |
|
465 |
|
466 //Method to calculate the distance this finger has moved since the downEvent |
|
467 function fingerHasMoved(finger: Touch_Finger): boolean; |
|
468 begin |
|
469 fingerHasMoved := trunc(sqrt(Power(finger.X-finger.historicalX,2) + Power(finger.y-finger.historicalY, 2))) > 330; |
|
470 end; |
|
471 |
|
472 function calculateDelta(finger1, finger2: Touch_Finger): hwFloat; inline; |
|
473 begin |
|
474 calculateDelta := DistanceI(finger2.x-finger1.x, finger2.y-finger1.y); |
|
475 end; |
|
476 |
|
477 // Under the premise that all pointer ids in pointerIds:SDL_FingerId are packed to the far left. |
|
478 // If the pointer to be ignored is not pointerIds[0] the second must be there |
|
479 function getSecondFinger(finger: Touch_Finger): PTouch_Finger; |
|
480 begin |
|
481 if fingers[0].id = finger.id then getSecondFinger := @fingers[1] |
|
482 else getSecondFinger := @fingers[0]; |
|
483 end; |
|
484 |
|
485 procedure printFinger(finger: Touch_Finger); |
|
486 begin |
|
487 WriteToConsole(Format('id:%d, (%d,%d), (%d,%d), time: %d', [finger.id, finger.x, finger.y, finger.historicalX, finger.historicalY, finger.timeSinceDown])); |
|
488 end; |
|
489 |
|
490 procedure initModule; |
|
491 var |
|
492 index: Longword; |
|
493 //uRenderCoordScaleX, uRenderCoordScaleY: Longword; |
|
494 begin |
|
495 movingCrosshair := false; |
|
496 stopFiring:= false; |
|
497 walkingLeft := false; |
|
498 walkingRight := false; |
|
499 |
|
500 leftButtonBoundary := cScreenWidth div 4; |
|
501 rightButtonBoundary := cScreenWidth div 4*3; |
|
502 topButtonBoundary := cScreenHeight div 6; |
|
503 |
|
504 setLength(fingers, 4); |
|
505 for index := 0 to High(fingers) do |
|
506 fingers[index].id := nilFingerId; |
|
507 |
|
508 |
|
509 //uRenderCoordScaleX := Round(cScreenWidth/0.8 * 2); |
|
510 fireButtonLeft := Round(cScreenWidth*0.01); |
|
511 fireButtonRight := Round(fireButtonLeft + (spritesData[sprFireButton].Width*0.4)); |
|
512 fireButtonBottom := Round(cScreenHeight*0.99); |
|
513 fireButtonTop := fireButtonBottom - Round(spritesData[sprFireButton].Height*0.4); |
|
514 end; |
|
515 |
|
516 begin |
|
517 end. |