hedgewars/uTouch.pas
changeset 6415 af2047bb4f70
parent 6344 cba81e10235c
child 6580 6155187bf599
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uTouch.pas	Thu Nov 24 16:18:45 2011 +0100
@@ -0,0 +1,517 @@
+(*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2011 Richard Deurwaarder <xeli@xelification.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *)
+
+{$INCLUDE "options.inc"}
+
+unit uTouch;
+
+interface
+
+uses sysutils, math, uConsole, uVariables, SDLh, uTypes, uFloat, uConsts, uIO, uCommands, GLUnit;
+
+// TODO: this type should be Int64
+// TODO: this type should be named TSDL_FingerId
+type SDL_FingerId = LongInt;
+
+type
+    PTouch_Finger = ^Touch_Finger;
+    Touch_Finger = record
+        id                       : SDL_FingerId;
+        x,y                      : LongInt;
+        historicalX, historicalY : LongInt;
+        timeSinceDown            : Longword;
+        end;
+
+procedure initModule;
+
+procedure ProcessTouch;
+procedure onTouchDown(x,y: Longword; pointerId: SDL_FingerId);
+procedure onTouchMotion(x,y: Longword; dx,dy: LongInt; pointerId: SDL_FingerId);
+procedure onTouchUp(x,y: Longword; pointerId: SDL_FingerId);
+function convertToCursor(scale: LongInt; xy: LongInt): LongInt;
+function addFinger(x,y: Longword; id: SDL_FingerId): PTouch_Finger;
+procedure deleteFinger(id: SDL_FingerId);
+procedure onTouchClick(finger: Touch_Finger);
+procedure onTouchDoubleClick(finger: Touch_Finger);
+
+function findFinger(id: SDL_FingerId): PTouch_Finger;
+procedure aim(finger: Touch_Finger);
+function isOnCrosshair(finger: Touch_Finger): boolean;
+function isOnCurrentHog(finger: Touch_Finger): boolean;
+function isOnFireButton(finger: Touch_Finger): boolean;
+procedure convertToWorldCoord(var x,y: hwFloat; finger: Touch_Finger);
+procedure convertToFingerCoord(var x,y: hwFloat; oldX, oldY: hwFloat);
+function fingerHasMoved(finger: Touch_Finger): boolean;
+function calculateDelta(finger1, finger2: Touch_Finger): hwFloat;
+function getSecondFinger(finger: Touch_Finger): PTouch_Finger;
+procedure printFinger(finger: Touch_Finger);
+implementation
+
+const
+    clicktime = 200;
+    nilFingerId = High(SDL_FingerId);
+
+var
+    fireButtonLeft, fireButtonRight, fireButtonTop, fireButtonBottom : LongInt;
+        
+
+
+    leftButtonBoundary  : LongInt;
+    rightButtonBoundary : LongInt;
+    topButtonBoundary   : LongInt;
+    
+    pointerCount : Longword;
+    fingers: array of Touch_Finger;
+    moveCursor : boolean;
+    invertCursor : boolean;
+
+    xTouchClick,yTouchClick : LongInt;
+    timeSinceClick : Longword;
+
+    //Pinch to zoom 
+    pinchSize : hwFloat;
+    baseZoomValue: GLFloat;
+
+
+    //aiming
+    aiming, movingCrosshair: boolean; 
+    crosshairCommand: ShortString;
+    targetAngle: LongInt;
+    stopFiring: boolean;
+
+    //moving
+    stopLeft, stopRight, walkingLeft, walkingRight :  boolean;
+
+
+procedure onTouchDown(x,y: Longword; pointerId: SDL_FingerId);
+var 
+    finger: PTouch_Finger;
+begin
+    finger := addFinger(x,y,pointerId);
+    case pointerCount of
+        1:
+        begin
+            moveCursor:= false;
+            if bShowAmmoMenu then
+            begin
+                moveCursor := true;
+                exit;
+            end;
+
+            if isOnCrosshair(finger^) then
+            begin
+                aiming:= true;
+                exit;
+            end;
+
+            if isOnFireButton(finger^) then
+            begin
+                stopFiring:= false;
+                ParseCommand('+attack', true);
+                exit;
+            end;
+            if (finger^.x < leftButtonBoundary) and (finger^.y < 390) then
+            begin
+                ParseCommand('+left', true);
+                walkingLeft := true;
+                exit;
+            end;
+            if finger^.x > rightButtonBoundary then
+            begin
+                ParseCommand('+right', true);
+                walkingRight:= true;
+                exit;
+            end;
+            if finger^.y < topButtonBoundary then
+            begin
+                ParseCommand('hjump', true);
+                exit;
+            end;
+            moveCursor:= true; 
+        end;
+        2:
+        begin
+            aiming:= false;
+            stopFiring:= true;
+            moveCursor:= false;
+            pinchSize := calculateDelta(finger^, getSecondFinger(finger^)^);
+            baseZoomValue := ZoomValue
+        end;
+    end;//end case pointerCount of
+end;
+
+procedure onTouchMotion(x,y: Longword;dx,dy: LongInt; pointerId: SDL_FingerId);
+var
+    finger, secondFinger: PTouch_Finger;
+    currentPinchDelta, zoom : hwFloat;
+    tmpX, tmpY: LongInt;
+begin
+    x := x;
+    y := y;
+    dx := dx;
+    dy := dy;
+    finger:= findFinger(pointerId);
+    tmpX := convertToCursor(cScreenWidth, x);
+    tmpY := convertToCursor(cScreenHeight, y);
+
+    if moveCursor then
+    begin
+        if invertCursor then
+        begin
+            CursorPoint.X := CursorPoint.X + (finger^.x - tmpX);
+            CursorPoint.Y := CursorPoint.Y - (finger^.y - tmpY);
+        end
+        else
+        begin
+            CursorPoint.X := CursorPoint.X - (finger^.x - tmpX);
+            CursorPoint.Y := CursorPoint.Y + (finger^.y - tmpY);
+        end;
+        finger^.x := tmpX;
+        finger^.y := tmpY;
+        exit //todo change into switch rather than ugly ifs
+    end;
+    
+    finger^.x := tmpX;
+    finger^.y := tmpY;
+    
+    if aiming then 
+    begin
+        aim(finger^);
+        exit
+    end;
+    if pointerCount = 2 then
+    begin
+       secondFinger := getSecondFinger(finger^);
+       currentPinchDelta := calculateDelta(finger^, secondFinger^) - pinchSize;
+       zoom := currentPinchDelta/cScreenWidth;
+       ZoomValue := baseZoomValue - ((hwFloat2Float(zoom) * cMinMaxZoomLevelDelta));
+       if ZoomValue < cMaxZoomLevel then ZoomValue := cMaxZoomLevel;
+       if ZoomValue > cMinZoomLevel then ZoomValue := cMinZoomLevel;
+    end;
+end;
+
+procedure onTouchUp(x,y: Longword; pointerId: SDL_FingerId);
+begin
+    x := x;
+    y := y;
+    aiming:= false;
+    stopFiring:= true;
+    deleteFinger(pointerId);
+
+    if walkingLeft then
+    begin
+        ParseCommand('-left', true);
+        walkingLeft := false;
+    end;
+
+    if walkingRight then
+    begin
+        ParseCommand('-right', true);
+        walkingRight := false;
+    end;
+end;
+
+procedure onTouchDoubleClick(finger: Touch_Finger);
+begin
+    finger := finger;//avoid compiler hint
+    ParseCommand('ljump', true);
+end;
+
+procedure onTouchClick(finger: Touch_Finger);
+begin
+    if (SDL_GetTicks - timeSinceClick < 300) and (DistanceI(finger.X-xTouchClick, finger.Y-yTouchClick) < _30) then
+    begin
+    onTouchDoubleClick(finger);
+    exit; 
+    end
+    else
+    begin
+        xTouchClick := finger.x;
+        yTouchClick := finger.y;
+        timeSinceClick := SDL_GetTicks;
+    end;
+
+    if bShowAmmoMenu then 
+    begin
+        doPut(CursorPoint.X, CursorPoint.Y, false); 
+        exit
+    end;
+
+    if isOnCurrentHog(finger) then
+    begin
+        bShowAmmoMenu := true;
+        exit;
+    end;
+
+    if finger.y < topButtonBoundary then
+    begin
+        ParseCommand('hjump', true);
+        exit;
+    end;
+end;
+
+function addFinger(x,y: Longword; id: SDL_FingerId): PTouch_Finger;
+var 
+    xCursor, yCursor, index : LongInt;
+begin
+    //Check array sizes
+    if length(fingers) < Integer(pointerCount) then 
+    begin
+        setLength(fingers, length(fingers)*2);
+        for index := length(fingers) div 2 to length(fingers) do fingers[index].id := nilFingerId;
+    end;
+    
+    
+    xCursor := convertToCursor(cScreenWidth, x);
+    yCursor := convertToCursor(cScreenHeight, y);
+    
+    //on removing fingers, all fingers are moved to the left
+    //with dynamic arrays being zero based, the new position of the finger is the old pointerCount
+    fingers[pointerCount].id := id;
+    fingers[pointerCount].historicalX := xCursor;
+    fingers[pointerCount].historicalY := yCursor;
+    fingers[pointerCount].x := xCursor;
+    fingers[pointerCount].y := yCursor;
+    fingers[pointerCount].timeSinceDown:= SDL_GetTicks;
+ 
+    addFinger:= @fingers[pointerCount];
+    inc(pointerCount);
+end;
+
+procedure deleteFinger(id: SDL_FingerId);
+var
+    index : Longword;
+begin
+    
+    dec(pointerCount);
+    for index := 0 to pointerCount do
+    begin
+         if fingers[index].id = id then
+         begin
+             //Check for onTouchClick event
+             if ((SDL_GetTicks - fingers[index].timeSinceDown) < clickTime) AND  
+                 not(fingerHasMoved(fingers[index])) then onTouchClick(fingers[index]);
+
+             //put the last finger into the spot of the finger to be removed, 
+             //so that all fingers are packed to the far left
+             if  pointerCount <> index then
+             begin
+                fingers[index].id := fingers[pointerCount].id;    
+                fingers[index].x := fingers[pointerCount].x;    
+                fingers[index].y := fingers[pointerCount].y;    
+                fingers[index].historicalX := fingers[pointerCount].historicalX;    
+                fingers[index].historicalY := fingers[pointerCount].historicalY;    
+                fingers[index].timeSinceDown := fingers[pointerCount].timeSinceDown;
+
+                fingers[pointerCount].id := nilFingerId;
+             end
+             else fingers[index].id := nilFingerId;
+             break;
+         end;
+    end;
+
+end;
+
+procedure ProcessTouch;
+var
+    deltaAngle: LongInt;
+begin
+    invertCursor := not(bShowAmmoMenu);
+    if aiming then
+    begin
+        if CurrentHedgehog^.Gear <> nil then
+        begin
+            deltaAngle:= CurrentHedgehog^.Gear^.Angle - targetAngle;
+            if (deltaAngle <> 0) and not(movingCrosshair) then 
+            begin
+                ParseCommand('+' + crosshairCommand, true);
+                movingCrosshair := true;
+            end
+            else 
+                if movingCrosshair then 
+                begin
+                    ParseCommand('-' + crosshairCommand, true);
+                    movingCrosshair:= false;
+                end;
+        end;
+    end
+    else if movingCrosshair then 
+    begin
+        ParseCommand('-' + crosshairCommand, true);
+        movingCrosshair := false;
+    end;
+
+    if stopFiring then 
+    begin
+        ParseCommand('-attack', true);
+        stopFiring:= false;
+    end;
+    
+    if stopRight then
+    begin
+        stopRight := false;
+        ParseCommand('-right', true);
+    end;
+ 
+    if stopLeft then
+    begin
+        stopLeft := false;
+        ParseCommand('-left', true);
+    end;
+    
+end;
+
+function findFinger(id: SDL_FingerId): PTouch_Finger;
+var
+    index: LongWord;
+begin
+   for index := 0 to High(fingers) do
+       if fingers[index].id = id then 
+       begin
+           findFinger := @fingers[index];
+           break;
+       end;
+end;
+
+procedure aim(finger: Touch_Finger);
+var 
+    hogX, hogY, touchX, touchY, deltaX, deltaY, tmpAngle: hwFloat;
+    tmp: ShortString;
+begin
+    if CurrentHedgehog^.Gear <> nil then
+    begin
+        touchX := _0;//avoid compiler hint
+        touchY := _0;
+        hogX := CurrentHedgehog^.Gear^.X;
+        hogY := CurrentHedgehog^.Gear^.Y;
+
+        convertToWorldCoord(touchX, touchY, finger);
+        deltaX := hwAbs(TouchX-HogX);
+        deltaY := (TouchY-HogY);
+        
+        tmpAngle:= DeltaY / Distance(deltaX, deltaY) *_2048;
+        targetAngle:= (hwRound(tmpAngle) + 2048) div 2;
+
+        tmp := crosshairCommand;
+        if CurrentHedgehog^.Gear^.Angle - targetAngle < 0 then crosshairCommand := 'down'
+        else crosshairCommand:= 'up';
+        if movingCrosshair and (tmp <> crosshairCommand) then 
+        begin
+            ParseCommand('-' + tmp, true);
+            movingCrosshair := false;
+        end;
+
+    end; //if CurrentHedgehog^.Gear <> nil
+end;
+
+function convertToCursor(scale: LongInt; xy: LongInt): LongInt;
+begin
+    convertToCursor := round(xy/32768*scale)
+end;
+
+function isOnFireButton(finger: Touch_Finger): boolean;
+begin
+    isOnFireButton:= (finger.x <= fireButtonRight) and (finger.x >= fireButtonLeft) and (finger.y <= fireButtonBottom) and (finger.y >= fireButtonTop);
+end;
+
+function isOnCrosshair(finger: Touch_Finger): boolean;
+var
+    x,y : hwFloat;
+begin
+    x := _0;//avoid compiler hint
+    y := _0;
+    convertToFingerCoord(x, y, int2hwFloat(CrosshairX), int2hwFloat(CrosshairY));
+    isOnCrosshair:= Distance(int2hwFloat(finger.x)-x, int2hwFloat(finger.y)-y) < _50;
+end;
+
+function isOnCurrentHog(finger: Touch_Finger): boolean;
+var
+    x,y : hwFloat;
+begin
+    x := _0;
+    y := _0;
+    convertToFingerCoord(x, y, CurrentHedgehog^.Gear^.X, CurrentHedgehog^.Gear^.Y);
+    isOnCurrentHog := Distance(int2hwFloat(finger.X)-x, int2hwFloat(finger.Y)-y) < _50;
+end;
+
+procedure convertToFingerCoord(var x,y : hwFloat; oldX, oldY: hwFloat);
+begin
+    x := oldX + int2hwFloat(WorldDx + (cScreenWidth div 2));
+    y := oldY + int2hwFloat(WorldDy);
+end;
+
+procedure convertToWorldCoord(var x,y: hwFloat; finger: Touch_Finger);
+begin
+//if x <> nil then 
+    x := int2hwFloat((finger.x-WorldDx) - (cScreenWidth div 2));
+//if y <> nil then 
+    y := int2hwFloat(finger.y-WorldDy);
+end;
+
+//Method to calculate the distance this finger has moved since the downEvent
+function fingerHasMoved(finger: Touch_Finger): boolean;
+begin
+    fingerHasMoved := trunc(sqrt(Power(finger.X-finger.historicalX,2) + Power(finger.y-finger.historicalY, 2))) > 330;
+end;
+
+function calculateDelta(finger1, finger2: Touch_Finger): hwFloat; inline;
+begin
+    calculateDelta := DistanceI(finger2.x-finger1.x, finger2.y-finger1.y);
+end;
+
+// Under the premise that all pointer ids in pointerIds:SDL_FingerId are packed to the far left.
+// If the pointer to be ignored is not pointerIds[0] the second must be there
+function getSecondFinger(finger: Touch_Finger): PTouch_Finger;
+begin
+    if fingers[0].id = finger.id then getSecondFinger := @fingers[1]
+    else getSecondFinger := @fingers[0];
+end;
+
+procedure printFinger(finger: Touch_Finger);
+begin
+    WriteToConsole(Format('id:%d, (%d,%d), (%d,%d), time: %d', [finger.id, finger.x, finger.y, finger.historicalX, finger.historicalY, finger.timeSinceDown]));
+end;
+
+procedure initModule;
+var
+    index: Longword;
+    //uRenderCoordScaleX, uRenderCoordScaleY: Longword;
+begin
+    movingCrosshair := false;
+    stopFiring:= false;
+    walkingLeft := false;
+    walkingRight := false;
+
+    leftButtonBoundary := cScreenWidth div 4;
+    rightButtonBoundary := cScreenWidth div 4*3;
+    topButtonBoundary := cScreenHeight div 6;
+    
+    setLength(fingers, 4);
+    for index := 0 to High(fingers) do 
+        fingers[index].id := nilFingerId;
+
+
+    //uRenderCoordScaleX := Round(cScreenWidth/0.8 * 2);
+    fireButtonLeft := Round(cScreenWidth*0.01);
+    fireButtonRight := Round(fireButtonLeft + (spritesData[sprFireButton].Width*0.4));
+    fireButtonBottom := Round(cScreenHeight*0.99);
+    fireButtonTop := fireButtonBottom - Round(spritesData[sprFireButton].Height*0.4);
+end;
+
+begin
+end.