disabling the discovery of SDL13+ on desktop. SDL13 has become SDL2 with a completely different ABI and will require a new FindSDL2 module for Cmake to be found; for current sdl development installations, hedgewars will either use the compatibility layer (present in sdl1.3 but not in sdl2) or just fail to build (in case sdl2 is installed but sdl1.2.* is not). whew
(*
* 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.