--- /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.