Adventure Game Studio | Forums

AGS Support => Modules, Plugins & Tools => Topic started by: Besh on Fri 09/06/2006 13:17:15

Title: MODULE: Mouse Gesture System v1.0
Post by: Besh on Fri 09/06/2006 13:17:15
MOUSE GESTURE SYSTEM script module

The idea of this system was born when I start to work to "Child of the Moon". I coded this to cast spell in a way that looks like "Black & White" miracles.

This first version recognizes 20 different gestures (4 lines, 8 circles, 8 arrows).

Download MGS 1.0 (http://www.webalice.it/besh81/MGS_v100.rar) (requires AGS 2.71 or later, not tested on previous versions)
Simple demo (http://www.webalice.it/besh81/MGS_demo.rar) (requires AGS 2.71 or later, not tested on previous versions)


// Script header for module 'Mouse Gesture System'
//
// Author: Gabriel Ferri (Besh)
//Ã,  Ã, Please contact me about problems with this module.
//
// Abstract: This system recognizes the mouse movements.
//
// Dependencies: AGS 2.71 or later (not tested on previous versions)
//
// Functions:
// MouseGesture.Activate(bool state)
//Ã,  Ã,  Ã,  Activates/deactivated Mouse Gesture System.
//
//Ã,  Ã, MouseGesture.SetMouseButton(MouseButton button)
//Ã,  Ã,  Ã,  Sets the mouse button used for the gesture. The default is LEFT mouse button.
// Actually, only LEFT and RIGHT mouse buttons are allowed.
//
//Ã,  Ã, MouseGesture.AddLine(GesturePoint direction, float tolerance, String name)
//Ã,  Ã,  Ã,  This adds a simple line gesture.
// direction: specifies the direction of the movement (eLeft, eRight, eUp, eDown).
// tolerance: is an absolute value used to simplify the movement (from 0.0 impossible to 4.0/5.0 simple)
// name: is the value returned by the system if this gesture is recognized.
//Ã,  Ã,  Ã, 
//Ã,  Ã, MouseGesture.AddCircle(GesturePoint start, GestureDirection direction, float tolerance, String name)
//Ã,  Ã,  Ã,  This adds a circle gesture.
// start: specifies the gesture starting point (eLeft, eRight, eUp, eDown).
// direction: specifies the direction of the movement (eClockwise, eCounterClockwise).
// tolerance: is an absolute value used to simplify the movement (from 0.0 impossible to 15.0/20.0 simple)
// name: is the value returned by the system if this gesture is recognized.
//
//Ã,  Ã, MouseGesture.AddArrow(GesturePoint arrowhead, GestureDirection direction, float tolerance, String name)
//Ã,  Ã,  Ã,  This adds an arrow gesture.
// arrowhead: specifies where the arrow is aiming (eLeft, eRight, eUp, eDown).
// direction: specifies the direction of the movement (eClockwise, eCounterClockwise).
// tolerance: is an absolute value used to simplify the movement (from 0.0 impossible to 10.0/15.0 simple)
// name: is the value returned by the system if this gesture is recognized.
//
// Srting MouseGesture.Name
// Contains the name of the recognized gesture.
//
// float MouseGesture.Vote
// Contains the vote of the recognized gesture (from 0.0 no gesture to 1.0 perfect gesture).
//
// bool MouseGesture.isGesture
// Sets to 1 when by the system when a new gesture is recognized.
//
//
//
// Use:
//Ã,  Ã,  Add gestures and give them a name. Remenber to activate the system.
//
// IMPORTANT - in repeaditely_execute function add:
// if (MouseGesture.isGesture) {
// MouseGesture.isGesture = false;
// ...
// ...
// }
//
// This code is needful to capture recognized gesture (not a great solution but works fine :-))
//
// Example:
// MouseGesture.AddArrow(eUp, eClockwise, 10.0, "ArrowUp");
// Ã,  MouseGesture.AddCircle(eRight, eCounterClockwise, 15.0, "Circle");
// Ã,  MouseGesture.AddLine(eLeft, 5.0, "LineSX");
// MouseGesture.AddLine(eRight, 5.0, "LineDX");
//
// ...
//
// //script for Room: Repeatedly execute
//Ã,  if (MouseGesture.isGesture) {
//Ã,  Ã,  MouseGesture.isGesture = false;
//Ã, 
// if (MouseGesture.Name == "ArrowUp")
// Function1();
// else if ((MouseGesture.Name == "Circle") && (MouseGesture.Vote >= 0.6))
// Function2();
// else if (MouseGesture.Name == "LineDX")
// Function3();
// else if (MouseGesture.Name == "")
// // no gesture recognized
// }
//
//
//
// Revision History:
//
// 09 Jun 06: v1.0Ã,  First release of Mouse Gesture System module
//
// Licence:
//
//Ã,  Ã, AGS Mouse Gesture System script module
//Ã,  Ã, Copyright (C) 2005-2006 Gabriel Ferri
//
//Ã,  Ã, This library is free software; you can redistribute it and/or
//Ã,  Ã, modify it under the terms of the GNU Lesser General Public
//Ã,  Ã, License as published by the Free Software Foundation; either
//Ã,  Ã, version 2.1 of the License, or (at your option) any later version.
//
//Ã,  Ã, This library 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
//Ã,  Ã, Lesser General Public License for more details.
//
//Ã,  Ã, You should have received a copy of the GNU Lesser General Public
//Ã,  Ã, License along with this library; if not, write to the Free Software
//Ã,  Ã, Foundation, Inc, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//=========================================================



What do you think? Let me know.
Title: Re: MODULE: Mouse Gesture System 1.0
Post by: SSH on Fri 09/06/2006 14:48:43
Looks excellent! Good work, besh!
Title: Re: MODULE: Mouse Gesture System 1.0
Post by: Radiant on Fri 09/06/2006 17:30:09
That's actually a rather nifty idea! I can see some cool games using this...
Title: Re: MODULE: Mouse Gesture System 1.0
Post by: lo_res_man on Fri 09/06/2006 18:41:16

I tried "child of the moon" and I found the mousing impossibly difficult, maybe some kind of mouse trail would help poor inepts like me.
Title: Re: MODULE: Mouse Gesture System 1.0
Post by: jasonjkay on Sat 10/06/2006 11:11:17
Looks good, i'll add it to my site around monday.
Title: Re: MODULE: Mouse Gesture System v1.0
Post by: magintz on Sat 08/07/2006 00:48:56
I had something similar in the works a while back where a verb coin type thing would appear and allow you to draw gestures within this GUI. I was working on it picking up angles of movement for general gestures and also speed, so that some puzzles may include throwing a ball or something and require speed and angle.

My main purpose of doing this was to have it like the firefox mouse gestures where "right click and drag" in various directions would perform simple actions such as look at or pick up emminating around the start point, which would be the hotspot...


This project however never progressed. I'm thinking of doing something using the drop down menu module but mixing it with a verb coin style and using context sensitive user actions, such as a window having open window and a candle having light or blow out.
Title: Re: MODULE: Mouse Gesture System v1.0
Post by: Babar on Sat 08/07/2006 14:09:48
Not sure if I understand what this does. Is it like the SPELLBOUND game that MrColossal made?
Title: Re: MODULE: Mouse Gesture System v1.0
Post by: cat on Tue 14/06/2022 20:33:21
Long time ago I made a few modifications to this module for my game Kanji Gakusei (https://www.adventuregamestudio.co.uk/forums/index.php?topic=55648). I forgot to upload the modifications which is required by the license so here are the files that my project uses:

MouseGestureSystem.ash
Code (ags) Select

// Script header for module 'Mouse Gesture System'
//
// Author: Gabriel Ferri (Besh)
//   Please contact me about problems with this module.
//
// Enhanced and converted to AGS 3.3.0 by cat
//
// Abstract: This system recognizes the mouse movements.
//
// Dependencies: AGS 3.3.0 or later (not tested on previous versions)
//
// Functions:
// MouseGesture.Activate(bool state)
//      Activates/deactivated Mouse Gesture System.
//
//   MouseGesture.SetMouseButton(MouseButton button)
//      Sets the mouse button used for the gesture. The default is LEFT mouse button.
// Actually, only LEFT and RIGHT mouse buttons are allowed.
//
//   MouseGesture.AddLine(GesturePoint direction, float tolerance, String name)
//      This adds a simple line gesture.
// direction: specifies the direction of the movement (eLeft, eRight, eUp, eDown).
// tolerance: is an absolute value used to simplify the movement (from 0.0 impossible to 4.0/5.0 simple)
// name: is the value returned by the system if this gesture is recognized.
//     
//   MouseGesture.AddCircle(GesturePoint start, GestureDirection direction, float tolerance, String name)
//      This adds a circle gesture.
// start: specifies the gesture starting point (eLeft, eRight, eUp, eDown).
// direction: specifies the direction of the movement (eClockwise, eCounterClockwise).
// tolerance: is an absolute value used to simplify the movement (from 0.0 impossible to 15.0/20.0 simple)
// name: is the value returned by the system if this gesture is recognized.
//
//   MouseGesture.AddArrow(GesturePoint arrowhead, GestureDirection direction, float tolerance, String name)
//      This adds an arrow gesture.
// arrowhead: specifies where the arrow is aiming (eLeft, eRight, eUp, eDown).
// direction: specifies the direction of the movement (eClockwise, eCounterClockwise).
// tolerance: is an absolute value used to simplify the movement (from 0.0 impossible to 10.0/15.0 simple)
// name: is the value returned by the system if this gesture is recognized.
//
// Srting MouseGesture.Name
// Contains the name of the recognized gesture.
//
// float MouseGesture.Vote
// Contains the vote of the recognized gesture (from 0.0 no gesture to 1.0 perfect gesture).
//
// bool MouseGesture.isGesture
// Sets to 1 when by the system when a new gesture is recognized.
//
//
//
// Use:
//    Add gestures and give them a name. Remenber to activate the system.
//
// IMPORTANT - in repeaditely_execute function add:
// if (MouseGesture.isGesture) {
// MouseGesture.isGesture = false;
// ...
// ...
// }
//
// This code is needful to capture recognized gesture (not a great solution but works fine :-))
//
// Example:
// MouseGesture.AddArrow(eUp, eClockwise, 10.0, "ArrowUp");
//   MouseGesture.AddCircle(eRight, eCounterClockwise, 15.0, "Circle");
//   MouseGesture.AddLine(eLeft, 5.0, "LineSX");
// MouseGesture.AddLine(eRight, 5.0, "LineDX");
//
// ...
//
// //script for Room: Repeatedly execute
//  if (MouseGesture.isGesture) {
//    MouseGesture.isGesture = false;
// 
// if (MouseGesture.Name == "ArrowUp")
// Function1();
// else if ((MouseGesture.Name == "Circle") && (MouseGesture.Vote >= 0.6))
// Function2();
// else if (MouseGesture.Name == "LineDX")
// Function3();
// else if (MouseGesture.Name == "")
// // no gesture recognized
// }
//
//
//
// Revision History:
//
// 09 Jun 06: v1.0  First release of Mouse Gesture System module
//
// Licence:
//
//   AGS Mouse Gesture System script module
//   Copyright (C) 2005-2006 Gabriel Ferri
//
//   This library is free software; you can redistribute it and/or
//   modify it under the terms of the GNU Lesser General Public
//   License as published by the Free Software Foundation; either
//   version 2.1 of the License, or (at your option) any later version.
//
//   This library 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
//   Lesser General Public License for more details.
//
//   You should have received a copy of the GNU Lesser General Public
//   License along with this library; if not, write to the Free Software
//   Foundation, Inc, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//=========================================================

enum GesturePoint { eUp, eDown, eRight, eLeft, eUndefined };
enum GestureDirection { eClockwise, eCounterClockwise };
enum GestureRegionViolationAction { eGestureNone, eGestureIgnore, eGestureFail };

autoptr managed struct GestureRegion
{
    int Top;
    int Right;
    int Bottom;
    int Left;
   
    import static GestureRegion Create(int top, int right, int bottom, int left);
};

struct sMouseGesture {
   import function SetWorkspace(GestureRegion workspace);
   import function Activate(bool state);
import function SetMouseButton(MouseButton button);
   import function SetRegion(GestureRegion gestureRegion);
import function AddLine(GesturePoint direction, float tolerance, String name, );
   import function AddCustomLine(float angle, float tolerance, String name);
import function AddCircle(GesturePoint start, GestureDirection direction, float tolerance, String name);
import function AddArrow(GesturePoint arrowhead, GestureDirection direction, float tolerance, String name);
   import function AddCustomArrow(float angles[], int arraySize, float tolerance, String name);
   import function Reset();

bool isGesture;
float Vote;
String Name;
  GestureRegionViolationAction RegionViolationAction;
};

import sMouseGesture MouseGesture;


MouseGestureSystem.asc
Code (ags) Select

// Main script for module 'Mouse Gesture System'
#define MAX_GESTURES 32

// internal parameters
#define MAX_POINTS 128
#define MIN_DISTANCE 15.0
#define MAX_SEGMENTS 32

// internal struct and variables
enum GestureShape { eLine, eCircle, eArrow, eUser };

struct GestureObj {
  GestureShape shape;
  GesturePoint point;
  GestureDirection direction;
  String name;
 
  float tolerance;
  float vangle[MAX_SEGMENTS]; //in degrees
  int vnum;
 
  float vote;
};

// module interface
sMouseGesture MouseGesture;
export MouseGesture;

// is Mouse Gesture System active
bool isGestureActive;

// mouse button that activate the gesture
MouseButton active_button;

// region where the gesture is valid
GestureRegion gestureRegion;

bool isGestureStarted;
int vx[MAX_POINTS];
int vy[MAX_POINTS];
float vangle[MAX_SEGMENTS];
int vnum;

GestureObj vGesture[MAX_GESTURES];
int vGesturenum;

// workspace area where clicks are handled
GestureRegion workspace;

// PUBLIC FUNCTIONS

static GestureRegion GestureRegion::Create(int top, int right, int bottom, int left)
{
    GestureRegion r = new GestureRegion;
    r.Top = top;
    r.Right = right;
    r.Bottom = bottom;
    r.Left = left;
    return r;
}

// ignore mouse clicks that are outside the workspace
function sMouseGesture::SetWorkspace(GestureRegion newWorkspace)
{
   workspace = newWorkspace;
}

function sMouseGesture::Activate (bool state) {
if (state) {
isGestureStarted = false;
MouseGesture.isGesture = false;
}

isGestureActive = state;
}

function sMouseGesture::Reset () {
   vnum = 0;
   vGesturenum = 0;
   isGestureActive = false;
   isGestureStarted = false;
   
   int i = 0;   
   while (i < MAX_GESTURES)
   {
      vGesture[i].direction = eClockwise;
      vGesture[i].name = "";
      vGesture[i].point = eUndefined;
      vGesture[i].shape = eUser;
      vGesture[i].tolerance = 0.0;
      vGesture[i].vnum = 0;
      vGesture[i].vote = 0.0;
     
      int j = 0;
      while (j < MAX_SEGMENTS)
      {
         vGesture[i].vangle[j] = 0.0;
         j++;
      }
     
      i++;
   }
}

function sMouseGesture::SetMouseButton(MouseButton button) {
  if (button == eMouseRight)
active_button = eMouseRight;
else
active_button = eMouseLeft;
}

function sMouseGesture::SetRegion(GestureRegion newRegion) {
   gestureRegion = newRegion;
}

function sMouseGesture::AddLine(GesturePoint direction, float tolerance, String name) {
  if (vGesturenum < MAX_GESTURES) {
vGesture[vGesturenum].shape = eLine;
vGesture[vGesturenum].point = direction;
vGesture[vGesturenum].tolerance = tolerance;
vGesture[vGesturenum].name = name;

if (direction == eRight) {
  vGesture[vGesturenum].vangle[0] = 90.0;
}
else if (direction == eLeft) {
  vGesture[vGesturenum].vangle[0] = -90.0;
}
else if (direction == eUp) {
  vGesture[vGesturenum].vangle[0] = 180.0;
}
else if (direction == eDown) {
  vGesture[vGesturenum].vangle[0] = 0.0;
}

vGesture[vGesturenum].vnum = 1;

vGesturenum += 1;
}
}

function sMouseGesture::AddCustomLine(float angle, float tolerance, String name) {
   if (vGesturenum < MAX_GESTURES) {
vGesture[vGesturenum].shape = eLine;
vGesture[vGesturenum].point = eUndefined;
vGesture[vGesturenum].tolerance = tolerance;
vGesture[vGesturenum].name = name;

vGesture[vGesturenum].vangle[0] = angle;

vGesture[vGesturenum].vnum = 1;

vGesturenum += 1;
}
}


function sMouseGesture::AddCircle(GesturePoint start, GestureDirection direction, float tolerance, String name) {
if (vGesturenum < MAX_GESTURES) {
vGesture[vGesturenum].shape = eCircle;
vGesture[vGesturenum].direction = direction;
vGesture[vGesturenum].point = start;
vGesture[vGesturenum].tolerance = tolerance;
vGesture[vGesturenum].name = name;

// i punti vengono generati a run-time
      // the points are generated at run-time
vGesture[vGesturenum].vnum = 0;

vGesturenum += 1;
}
}


function sMouseGesture::AddArrow(GesturePoint arrowhead, GestureDirection direction, float tolerance, String name) {
if (vGesturenum < MAX_GESTURES) {
vGesture[vGesturenum].shape = eArrow;
vGesture[vGesturenum].direction = direction;
vGesture[vGesturenum].point = arrowhead;
vGesture[vGesturenum].tolerance = tolerance;
vGesture[vGesturenum].name = name;

if (arrowhead == eRight) {  
if (direction == eClockwise) {
  vGesture[vGesturenum].vangle[0] = 45.0;
  vGesture[vGesturenum].vangle[1] = -45.0;
}
else {
  vGesture[vGesturenum].vangle[0] = 135.0;
  vGesture[vGesturenum].vangle[1] = -135.0;
}
}
else if (arrowhead == eLeft) {
if (direction == eClockwise) {
  vGesture[vGesturenum].vangle[0] = -135.0;
  vGesture[vGesturenum].vangle[1] = 135.0;
}
else {
  vGesture[vGesturenum].vangle[0] = -45.0;
  vGesture[vGesturenum].vangle[1] = 45.0;
}
}
else if (arrowhead == eUp) {
if (direction == eClockwise) {
  vGesture[vGesturenum].vangle[0] = 135.0;
  vGesture[vGesturenum].vangle[1] = 45.0;
}
else {
  vGesture[vGesturenum].vangle[0] = -135.0;
  vGesture[vGesturenum].vangle[1] = -45.0;
}
}
else if (arrowhead == eDown) {
if (direction == eClockwise) {
  vGesture[vGesturenum].vangle[0] = -45.0;
  vGesture[vGesturenum].vangle[1] = -135.0;
}
else {
  vGesture[vGesturenum].vangle[0] = 45.0;
  vGesture[vGesturenum].vangle[1] = 135.0;
}
}
vGesture[vGesturenum].vnum = 2;

vGesturenum += 1;
}
}

function sMouseGesture::AddCustomArrow(float angles[], int arraySize, float tolerance, String name) {
   if (vGesturenum < MAX_GESTURES) {
vGesture[vGesturenum].shape = eArrow;
      vGesture[vGesturenum].direction = eClockwise;
vGesture[vGesturenum].point = eUndefined;
vGesture[vGesturenum].tolerance = tolerance;
vGesture[vGesturenum].name = name;

      int i = 0;
      while (i < arraySize)
      {
         vGesture[vGesturenum].vangle[i] = angles[i];
         i++;
      }
     
vGesture[vGesturenum].vnum = arraySize;

vGesturenum += 1;
}
}

// INTERNAL FUNCTIONS
function vReduction() {
  int x_min = 320;
  int y_min = 240;
  int x_max = -1;
  int y_max = -1;

int i = 0;
  while (i < vnum) {
    if (x_min > vx[i])
x_min = vx[i];
if (x_max < vx[i])
x_max = vx[i];

if (y_min > vy[i])
y_min = vy[i];
if (y_max < vy[i])
y_max = vy[i];

i += 1;
}

float scale;
if (x_max - x_min > y_max - y_min)
scale = IntToFloat(x_max - x_min) / 100.0;
else
scale = IntToFloat(y_max - y_min) / 100.0;

//
vx[0] = FloatToInt(IntToFloat(vx[0] - x_min) / scale);
  vy[0] = FloatToInt(IntToFloat(vy[0] - y_min) / scale);

i = 1;
int c = 1;
int x, y;
  while (i < vnum && c < MAX_SEGMENTS + 1) {
    x = FloatToInt(IntToFloat(vx[i] - x_min) / scale);
    y = FloatToInt(IntToFloat(vy[i] - y_min) / scale);
   
    if (Maths.Sqrt(IntToFloat((x - vx[c - 1]) * (x - vx[c - 1]) + (y - vy[c - 1]) * (y - vy[c - 1]))) >= MIN_DISTANCE) {
  vx[c] = x;
  vy[c] = y;
 
  vangle[c - 1] = Maths.ArcTan2(IntToFloat(x - vx[c - 1]), IntToFloat(y - vy[c - 1]));
  vangle[c - 1] = Maths.RadiansToDegrees(vangle[c - 1]);

  c += 1;
}
else if (i + 1 == vnum) { //è l'ultimo // it's the last
  vx[c] = x;
  vy[c] = y;
 
  vangle[c - 1] = Maths.ArcTan2(IntToFloat(x - vx[c - 1]), IntToFloat(y - vy[c - 1]));
 
  c += 1;  
}

    i += 1;
}
vnum = c - 1;
}


float vote;
function Vote(float a, float b, float tol) {
  float delta = a - b;
 
  // control
if (delta < -180.0)
delta += 360.0;
else if (delta > 180.0)
delta -= 360.0;
 
  // control sign
  if (delta < 0.0)
delta = -delta;

if (delta < tol / 2.0)
vote = 1.0;
if (delta < tol)
vote = 1.0 - delta / (10.0 * tol);
else
  vote = 1.0 - delta / (4.0 * tol);
}


function FindGesture() {
int c, k, i;
float votes[MAX_SEGMENTS];
int types[MAX_SEGMENTS];

i = 0;
while (i < vGesturenum) {
  // clear votes & types
  c = 0;
  while (c < vnum - 1) {
    votes[c] = 0.0;
    types[c] = -1;
    c += 1;
  }  
 
  // LINE
  if (vGesture[i].shape == eLine) {
    // voting
    c = 0;
    while (c < vnum - 1) {
      Vote(vGesture[i].vangle[0], vangle[c], vGesture[i].tolerance);
      votes[c] = vote;

c += 1;
}

// voto medio
vGesture[i].vote = 0.0;
c = 0;
while (c < vnum - 1) {
  vGesture[i].vote += votes[c];
  c += 1;
}
vGesture[i].vote = vGesture[i].vote / IntToFloat(vnum - 1);

// controllo forma
}
// CIRCLE
  else if (vGesture[i].shape == eCircle) {
    if (vnum - 1 >= 8 || vnum - 1 >= 26) {
// genera cerchio in base ai segmenti ottenuti
float delta_angle = 360.0 / IntToFloat(vnum - 1);
float current_angle;

// angolo di partenza
if (vGesture[i].point == eUp)
current_angle = 90.0;
else if (vGesture[i].point == eRight)
current_angle = 0.0;
else if (vGesture[i].point == eDown)
current_angle = -90.0;
else if (vGesture[i].point == eLeft)
current_angle = 180.0;
   
// direzione
if (vGesture[i].direction == eClockwise) {
delta_angle = -delta_angle;
current_angle += delta_angle;
}
else
current_angle = current_angle - 180.0 + delta_angle;
   
// control
if (current_angle < -180.0)
current_angle += 360.0;
else if (current_angle > 180.0)
current_angle -= 360.0;

// voting
c = 0;
while (c < vnum - 1) {
Vote(current_angle, vangle[c], vGesture[i].tolerance);
votes[c] = vote;
     
//update angle
current_angle += delta_angle;

// control
if (current_angle < -180.0)
current_angle += 360.0;
else if (current_angle > 180.0)
current_angle -= 360.0;

c += 1;
}

// voto medio
vGesture[i].vote = 0.0;
c = 0;
while (c < vnum - 1) {
vGesture[i].vote += votes[c];
c += 1;
}
vGesture[i].vote = vGesture[i].vote / IntToFloat(vnum - 1);

// controllo forma
if (vnum - 1 < 14 || vnum - 1 > 20)
vGesture[i].vote = vGesture[i].vote * 0.25;
}
else
vGesture[i].vote = 0.0;
}
// ARROW
else if (vGesture[i].shape == eArrow) {
    // voting
    c = 0;
    k = 0;
    while (c < vnum - 1 && k < vGesture[i].vnum) {
      Vote(vGesture[i].vangle[k], vangle[c], vGesture[i].tolerance);
      votes[c] = vote;
     
if (vote < 0.2) {
  types[k] = c;
k += 1;
c -= 1;
}

c += 1;
}

// voto medio
vGesture[i].vote = 0.0;
c = 0;
while (c < vnum - 1) {
  vGesture[i].vote += votes[c];
  c += 1;
}
vGesture[i].vote = vGesture[i].vote / IntToFloat(vnum - 1);

// controllo forma
if (types[0] <= 0 || types[1] > -1)
vGesture[i].vote = 0.0;
else {
  float a = IntToFloat(types[0]);

  if ((vnum - 1 - types[0]) == 0)
vGesture[i].vote = 0.0;
else {
  float b = IntToFloat(vnum - 1 - types[0]);

if (a / b > 1.4 || b / a > 1.4)
vGesture[i].vote = vGesture[i].vote * 0.25;
}
}
}
else { // eUser
}

i += 1;
}
}


int winner;
function FindWinner() {
int i = 0;
float max_vote = 0.0;
winner = -1;

while (i < vGesturenum) {
  if (vGesture[i].vote > max_vote) {
    max_vote = vGesture[i].vote;
    winner = i;
}

i += 1;
}

if (winner != -1) {
MouseGesture.Vote = max_vote;
MouseGesture.Name = vGesture[winner].name;
}
else {
  MouseGesture.Vote = 0.0;
MouseGesture.Name = "";
}

MouseGesture.isGesture = true;
}


function game_start () {
  // init variables
  isGestureActive = false;
  isGestureStarted = false;
  vnum = 0;
  vGesturenum = 0;
  active_button = eMouseLeft;
  MouseGesture.RegionViolationAction = eGestureNone;
}

function isOutsideRegion(int x, int y) {
   if (gestureRegion != null &&
      (x < gestureRegion.Left || x > gestureRegion.Right || y < gestureRegion.Top || y > gestureRegion.Bottom)) {
      return true;
   } else {
      return false;
   }
}

bool isRegionFail = false;

function repeatedly_execute () {
   if (isGestureActive) {
      if (mouse.IsButtonDown(active_button) && isGestureStarted) {
         if (MouseGesture.RegionViolationAction == eGestureIgnore && isOutsideRegion(mouse.x, mouse.y)) {
            // Will be ignored, do nothing here
         } else if (MouseGesture.RegionViolationAction == eGestureFail && isOutsideRegion(mouse.x, mouse.y)) {
            // Remember that it is a fail, but don't stop immediately
            isRegionFail = true;
         }
         // standard behaviour
         else if ((vnum < MAX_POINTS) && ((vx[vnum - 1] != mouse.x) || (vy[vnum - 1] != mouse.y))) {  
            vx[vnum] = mouse.x;
            vy[vnum] = mouse.y;

            vnum += 1;
         }
      }
      else if (isGestureStarted) {
         isGestureStarted = false;

         // Gesture fails immediately
         if (isRegionFail)
         {
            isRegionFail = false;
            MouseGesture.Vote = 0.0;
            MouseGesture.Name = "";
            MouseGesture.isGesture = true;
         }
         else if (vnum > 3) {
            vReduction();

            FindGesture();
            FindWinner();
         }
      }
   }
}

function isOutsideWorkspace(int x, int y) {
   if (workspace != null &&
      (x < workspace.Left || x > workspace.Right || y < workspace.Top || y > workspace.Bottom)) {
      return true;
   } else {
      return false;
   }
}

function on_mouse_click(MouseButton button) {
   if (IsGamePaused() != 1 && isGestureActive && button == active_button) {
      if (!isOutsideWorkspace(mouse.x, mouse.y))
      {
         vx[0] = mouse.x;
         vy[0] = mouse.y;

         vnum = 1;   
         isGestureStarted = true;
      }
   }
}