isOnCurrentHog and isOnCrosshair now look at the distance relative to the screen rather than world coords, this means that zooming out doesn't make it harder to touch the hog or crosshair
(*
* 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, uCommandHandlers;
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
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
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
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) < 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 : Longint;
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
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
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
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, 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.