Fixes slot sprite and ammo sprites overlapping left side border. There is still the issue that boxes should be 32px between borders, but right now they are 33px on all but the first row (since the outside border overlaps it by 1px) causing the slot sprite to have 2px of border on the left and 1px of border on the right.
(*
* 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;
dx,dy : 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 convertToCursorX(x: LongInt): LongInt;
function convertToCursorY(y: LongInt): LongInt;
function convertToCursorDeltaX(x: LongInt): LongInt;
function convertToCursorDeltaY(y: LongInt): LongInt;
function addFinger(x,y: Longword; id: SDL_FingerId): PTouch_Finger;
function updateFinger(x,y,dx,dy: 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;
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;
function isOnRect(x,y,w,h: LongInt; finger: Touch_Finger): boolean;
procedure printFinger(finger: Touch_Finger);
implementation
const
clicktime = 200;
nilFingerId = High(SDL_FingerId);
var
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;
aimingUp, aimingDown: 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 isOnRect(fireButtonX, fireButtonY, fireButtonW, fireButtonH, finger^) then
begin
stopFiring:= false;
ParseCommand('+attack', true);
exit;
end;
if isOnRect(arrowLeftX, arrowLeftY, arrowLeftW, arrowLeftH, finger^) then
begin
ParseCommand('+left', true);
walkingLeft := true;
exit;
end;
if isOnRect(arrowRightX, arrowRightY, arrowRightW, arrowRightH, finger^) then
begin
ParseCommand('+right', true);
walkingRight:= true;
exit;
end;
if isOnRect(arrowUpX, arrowUpY, arrowUpW, arrowUpH, finger^) then
begin
ParseCommand('+up', true);
aimingUp:= true;
exit;
end;
if isOnRect(arrowDownX, arrowDownY, arrowUpW, arrowUpH, finger^) then
begin
ParseCommand('+down', true);
aimingDown:= true;
exit;
end;
if isOnRect(backjumpX, backjumpY, backjumpW, backjumpH, finger^) then
begin
ParseCommand('hjump', true);
exit;
end;
if isOnRect(forwardjumpX, forwardjumpY, forwardjumpW, forwardjumpH, finger^) then
begin
ParseCommand('ljump', 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;
begin
finger:= updateFinger(x,y,dx,dy,pointerId);
if moveCursor then
begin
if invertCursor then
begin
CursorPoint.X := CursorPoint.X - finger^.dx;
CursorPoint.Y := CursorPoint.Y + finger^.dy;
end
else
begin
CursorPoint.X := CursorPoint.X + finger^.dx;
CursorPoint.Y := CursorPoint.Y - finger^.dy;
end;
exit //todo change into switch rather than ugly ifs
end;
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);
var
finger: PTouch_Finger;
begin
x := x;
y := y;
aiming:= false;
stopFiring:= true;
finger:= updateFinger(x,y,0,0,pointerId);
//Check for onTouchClick event
if ((SDL_GetTicks - finger^.timeSinceDown) < clickTime) AND not(fingerHasMoved(finger^)) then
onTouchClick(finger^);
deleteFinger(pointerId);
if walkingLeft then
begin
ParseCommand('-left', true);
walkingLeft := false;
end;
if walkingRight then
begin
ParseCommand('-right', true);
walkingRight := false;
end;
if aimingUp then
begin
ParseCommand('-up', true);
aimingUp:= false;
end;
if aimingDown then
begin
ParseCommand('-down', true);
aimingDown:= 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);
timeSinceClick:= -1;
exit;
end;
xTouchClick:= finger.x;
yTouchClick:= finger.y;
timeSinceClick:= SDL_GetTicks;
if bShowAmmoMenu then
begin
CursorPoint.X:= finger.x;
CursorPoint.Y:= finger.y;
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 := convertToCursorX(x);
yCursor := convertToCursorY(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].dx := 0;
fingers[pointerCount].dy := 0;
fingers[pointerCount].timeSinceDown:= SDL_GetTicks;
addFinger:= @fingers[pointerCount];
inc(pointerCount);
end;
function updateFinger(x,y,dx,dy: Longword; id: SDL_FingerId): PTouch_Finger;
begin
updateFinger:= findFinger(id);
updateFinger^.x:= convertToCursorX(x);
updateFinger^.y:= convertToCursorY(y);
updateFinger^.dx:= convertToCursorDeltaX(dx);
updateFinger^.dy:= convertToCursorDeltaY(dy);
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
//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 := 'up'
else
crosshairCommand:= 'down';
if movingCrosshair and (tmp <> crosshairCommand) then
begin
ParseCommand('-' + tmp, true);
movingCrosshair := false;
end;
end; //if CurrentHedgehog^.Gear <> nil
end;
//These 4 convertToCursor functions convert xy coords from the SDL coordinate system to our CursorPoint coor system
// the SDL coordinate system goes from 0 to 32768 on the x axis and 0 to 32768 on the y axis, (0,0) being top left.
// the CursorPoint coordinate system goes from -cScreenWidth/2 to cScreenWidth/2 on the x axis
// and 0 to cScreenHeight on the x axis, (-cScreenWidth, cScreenHeight) being top left,
function convertToCursorX(x: LongInt): LongInt;
begin
convertToCursorX := round((x/32768)*cScreenWidth) - (cScreenWidth shr 1);
end;
function convertToCursorY(y: LongInt): LongInt;
begin
convertToCursorY := cScreenHeight - round((y/32768)*cScreenHeight)
end;
function convertToCursorDeltaX(x: LongInt): LongInt;
begin
convertToCursorDeltaX := round(x/32768*cScreenWidth)
end;
function convertToCursorDeltaY(y: LongInt): LongInt;
begin
convertToCursorDeltaY := round(y/32768*cScreenHeight)
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);
y := int2hwFloat(cScreenHeight) - (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;
function isOnRect(x,y,w,h: LongInt; finger: Touch_Finger): boolean;
begin
WriteToConsole(Format('(%d,%d) (%d, %d) %d',[finger.x, finger.y, x,y, w]));
isOnRect:= (finger.x > x) and
(finger.x < x+w) and
(cScreenHeight - finger.y > y) and
(cScreenHeight - finger.y < (y+w));
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;
setLength(fingers, 4);
for index := 0 to High(fingers) do
fingers[index].id := nilFingerId;
end;
begin
end.