hedgewars/uTouch.pas
changeset 6415 af2047bb4f70
parent 6344 cba81e10235c
child 6580 6155187bf599
equal deleted inserted replaced
6414:8474b7fa84d6 6415:af2047bb4f70
       
     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.