hedgewars/uTouch.pas
author Xeli
Thu, 18 Aug 2011 22:37:23 +0200
branchhedgeroid
changeset 5599 2e4b90f33a83
parent 5595 480d451152a5
child 5605 31bd6e30df02
permissions -rw-r--r--
aiming fixed, inverted cursor on ammo menu, added equal and isZero function to uFloat, changed the way ammo menu opens, you must now click on the hog rather than anywhere on the screen

{$INCLUDE "options.inc"}

unit uTouch;

interface

uses sysutils, math, uConsole, uVariables, SDLh, uTypes, uFloat, uConsts, uIO, uCommands, GLUnit, uCommandHandlers;

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;
procedure addFinger(x,y: Longword; id: SDL_FingerId);
procedure deleteFinger(id: SDL_FingerId);
procedure onTouchClick(x,y: Longword; pointerId: SDL_FingerId);

procedure aim(id: SDL_FingerId);
function isOnCurrentHog(id: SDL_FingerId): boolean;
procedure convertToWorldCoord(var x,y: hwFloat; id: SDL_FingerId);
function fingerHasMoved(id: SDL_FingerId): boolean;
function calculateDelta(id1, id2: SDL_FingerId): hwFloat;
function getSecondPointer(id: SDL_FingerId): SDL_FingerId;
implementation

const
    clicktime = 200;

var
    pointerCount : Longword;
    xyCoord : array of LongInt;
    pointerIds : array of SDL_FingerId;
    timeSinceDown: array of Longword;
    historicalXY : array of LongInt;
    //Pinch to zoom 
    pinchSize : hwFloat;
    baseZoomValue: GLFloat;

    invertCursor : boolean;

    //aiming
    aiming, movingCrosshair: boolean; 
    crosshairCommand: ShortString;
    aimingPointerId: SDL_FingerId;
    targetAngle: LongInt;    

procedure onTouchDown(x,y: Longword; pointerId: SDL_FingerId);
begin
    addFinger(x,y,pointerId);
    xyCoord[pointerId*2] := convertToCursor(cScreenWidth,x);
    xyCoord[pointerId*2+1] := convertToCursor(cScreenHeight,y);
    
   
    case pointerCount of
        1:
            if isOnCurrentHog(pointerId) then aiming:= true;
        2:
        begin
            aiming:= false;
            
            pinchSize := calculateDelta(pointerId, getSecondPointer(pointerId));
            baseZoomValue := ZoomValue
        end;
    end;//end case pointerCount of
end;

procedure onTouchMotion(x,y: Longword;dx,dy: LongInt; pointerId: SDL_FingerId);
var
    secondId : SDL_FingerId;
    currentPinchDelta, zoom : hwFloat;
begin
    xyCoord[pointerId*2] := convertToCursor(cScreenWidth, x);
    xyCoord[pointerId*2+1] := convertToCursor(cScreenHeight, y);
    
    case pointerCount of
       1:
           begin
               if aiming then 
               begin
                   aim(pointerId);
                   exit
               end;
               if invertCursor then
               begin
                   CursorPoint.X := CursorPoint.X - convertToCursor(cScreenWidth,dx);
                   CursorPoint.Y := CursorPoint.Y + convertToCursor(cScreenWidth,dy);
               end
               else
               begin
                   CursorPoint.X := CursorPoint.X + convertToCursor(cScreenWidth,dx);
                   CursorPoint.Y := CursorPoint.Y - convertToCursor(cScreenWidth,dy);
               end;
           end;
       2:
           begin
               secondId := getSecondPointer(pointerId);
               currentPinchDelta := calculateDelta(pointerId, secondId) - pinchSize;
               zoom := currentPinchDelta/cScreenWidth;
               ZoomValue := baseZoomValue - ((hwFloat2Float(zoom) * cMinMaxZoomLevelDelta));
               WriteToConsole(Format('Zoom in/out. ZoomValue = %f, %f', [ZoomValue, cMaxZoomLevel]));
               if ZoomValue > cMaxZoomLevel then ZoomValue := cMaxZoomLevel;
//               if ZoomValue < cMinZoomLevel then ZoomValue := cMinZoomLevel;
            end;
    end; //end case pointerCount of
end;

procedure onTouchUp(x,y: Longword; pointerId: SDL_FingerId);
begin
    aiming:= false;
    pointerCount := pointerCount-1;
    deleteFinger(pointerId);
end;

procedure onTouchClick(x,y: Longword; pointerId: SDL_FingerId);
begin
    if bShowAmmoMenu then 
    begin
        doPut(CursorPoint.X, CursorPoint.Y, false); 
        exit
    end;

    if isOnCurrentHog(pointerId) then
    begin
    bShowAmmoMenu := true;
    end;
    //WriteToConsole(Format('%s, %s : %d, %d', [cstr(CurrentHedgehog^.Gear^.X), cstr(CurrentHedgehog^.Gear^.Y), x-WorldDX, y-WorldDY]));
end;

procedure addFinger(x,y: Longword; id: SDL_FingerId);
var 
    index, tmp: Longword;
begin
    pointerCount := pointerCount + 1;

    //Check array sizes
    if length(pointerIds) < pointerCount then setLength(pointerIds, length(pointerIds)*2);
    if length(xyCoord) < id*2+1 then 
    begin 
        setLength(xyCoord, id*2+1);
        setLength(historicalXY, id*2+1);
    end;
    if length(timeSinceDown) < id then setLength(timeSinceDown, id); 
    for index := 0 to pointerCount do //place the pointer ids as far back to the left as possible
    begin
        if pointerIds[index] = -1 then 
           begin
               pointerIds[index] := id;
               break;
           end;
    end;
    //set timestamp
    timeSinceDown[id] := SDL_GetTicks;
    historicalXY[id*2] := convertToCursor(cScreenWidth,x);
    historicalXY[id*2+1] := convertToCursor(cScreenHeight,y);
end;

procedure deleteFinger(id: SDL_FingerId);
var
    index, i : Longint;
begin
    index := 0;
    for index := 0 to pointerCount do
    begin
         if pointerIds[index] = id then
         begin
             pointerIds[index] := -1;
             break;
         end;
    end;
    //put the last pointerId into the stop of the id to be removed, so that all pointerIds are to the far left
    for i := pointerCount downto index do
    begin
        if pointerIds[i] <> -1 then
        begin
            pointerIds[index] := pointerIds[i];
            break;
        end;
    end;
    if ((SDL_GetTicks - timeSinceDown[id]) < clickTime) AND  not(fingerHasMoved(id)) then onTouchClick(xyCoord[id*2], xyCoord[id*2+1], id);
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;
end;

procedure aim(id: SDL_FingerId);
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, id);
        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 isOnCurrentHog(id: SDL_FingerId): boolean;
var
    x,y, fingerX, fingerY : hwFloat;
begin
    x := CurrentHedgehog^.Gear^.X;
    y := CurrentHedgehog^.Gear^.Y;

    convertToWorldCoord(fingerX, fingerY, id);
    isOnCurrentHog := Distance(fingerX-x, fingerY-y) < _20;
end;

procedure convertToWorldCoord(var x,y: hwFloat; id: SDL_FingerId);
begin
//if x <> nil then 
    x := int2hwFloat((xyCoord[id*2]-WorldDx) - (cScreenWidth div 2));
//if y <> nil then 
    y := int2hwFloat(xyCoord[id*2+1]-WorldDy);
end;

//Method to calculate the distance this finger has moved since the downEvent
function fingerHasMoved(id: SDL_FingerId): boolean;
begin
//    fingerHasMoved := hwAbs(DistanceI(xyCoord[id*2]-historicalXY[id*2], xyCoord[id*2+1]-historicalXY[id*2+1])) > int2hwFloat(2000); // is 1% movement
    fingerHasMoved := trunc(sqrt(Power(xyCoord[id*2]-historicalXY[id*2],2) + Power(xyCoord[id*2+1]-historicalXY[id*2+1], 2))) > 330;
end;

function calculateDelta(id1, id2: SDL_FingerId): hwFloat;
begin
//    calculateDelta := Distance(xyCoord[id2*2] - xyCoord[id1*2], xyCoord[id2*2+1] - xyCoord[id1*2+1]);
    calculateDelta := int2hwFloat(trunc(sqrt(Power(xyCoord[id2*2]-xyCoord[id1*2],2) + Power(xyCoord[id2*2+1]-xyCoord[id1*2+1], 2))));
end;

// Under the premise that all pointer ids in pointerIds:SDL_FingerId are pack to the far left.
// If the pointer to be ignored is not pointerIds[0] the second must be there
function getSecondPointer(id: SDL_FingerId): SDL_FingerId;
begin
    if pointerIds[0] = id then getSecondPointer := pointerIds[1]
    else getSecondPointer := pointerIds[0];
end;

procedure initModule;
var
    index: Longword;
begin
    setLength(xyCoord, 10);
    setLength(pointerIds, 5);
    setLength(timeSinceDown, 5);
    setLength(historicalXY, 10);    
    for index := Low(xyCoord) to High(xyCoord) do xyCoord[index] := -1;
    for index := Low(pointerIds) to High(pointerIds) do pointerIds[index] := -1;
    movingCrosshair := false;
end;

begin
end.