Adventure Game Studio

AGS Support => Advanced Technical Forum => Topic started by: rmonic79 on Sat 09/06/2018 11:28:53

Title: Strategic turn based with stealth mechanic - SOLVED
Post by: rmonic79 on Sat 09/06/2018 11:28:53
Hi guys maybe you can help me, i'm trying to do a little variant in my game with strategic turn based mechanics. I can't realize an efficient way to make stealth mechanics.For example i have an overhead map with open building, how can i avoid  character to be seen if it's near and in line of sight but on other side of the wall? For one example is simple but the map is near complex. If you need i can post you a screenshot.
Thanks as always.
Title: Re: Strategic turn based with stealth mechanic
Post by: Khris on Sat 09/06/2018 11:57:29
There's no one answer to this question. What exactly do you mean by "near complex"? What are you using for walls?
Turn-based often also means grid-based, which means walls are along grid lines. According LoS algorithms are all over the internet.
Title: Re: Strategic turn based with stealth mechanic
Post by: Crimson Wizard on Sat 09/06/2018 12:49:55
Example: https://www.codeproject.com/Articles/15604/Ray-casting-in-a-2D-tile-based-environment
Title: Re: Strategic turn based with stealth mechanic
Post by: rmonic79 on Sat 09/06/2018 14:14:54
sorry i mean quite complex, now there's only a walkable area mask imported from sketch, put the walls of the labyrinth as an object i think it's the first thing to do, no there's no grid, the movement are based on dinstance from character.x.y. Interesting article about 2d raycasting by the way.
Title: Re: Strategic turn based with stealth mechanic
Post by: Khris on Sat 09/06/2018 16:10:17
Ok, so pixel-based? In that case you need to "walk" along the line from enemy to player and check every (or every other) pixel for being a wall, using a loop. Something like
bool SightBlocked(float fx, float fy) {
  int x = FloatToInt(fx, eRoundNearest);
  int y = FloatToInt(fy, eRoundNearest);
  // check if room pixel x, y is blocking
  return GetLocationType(x - GetViewportX(), y - GetViewportY()) == eLocationObject;
}

bool IsVisibleTo(this* Character, Character* enemy) {
  float x = IntToFloat(enemy.x), y = IntToFloat(enemy.y);
  float dx = IntToFloat(this.x - enemy.x);
  float dy = IntToFloat(this.y - enemy.y);
  float l = Math.sqrt(dx * dx + dy * dy);
  // check every 2 pixels
  dx /= l / 2.0;
  dy /= l / 2.0;
  int steps = FloatToInt(l / 2.0, eRoundDown);
  for (int i = 0; i < steps; i++) {
    x += dx; y += dy;
    if (SightBlocked(x, y)) return false;
  }
  return true;
}


Now you can use
  if (player.IsVisibleTo(cPatrol1)) ...
Title: Re: Strategic turn based with stealth mechanic
Post by: rmonic79 on Sat 09/06/2018 19:40:02
(http://i.imgur.com/HXz7pTGl.png?2) (https://imgur.com/HXz7pTG)

i had this error

EDIT:
Seems to be solved this way
(http://i.imgur.com/CN0JLKYl.png?1) (https://imgur.com/CN0JLKY)
Title: Re: Strategic turn based with stealth mechanic
Post by: Khris on Sun 10/06/2018 00:41:37
Was a typo:

    float l = Math.sqrt(dx * dx + dy * dy);
Title: Re: Strategic turn based with stealth mechanic
Post by: rmonic79 on Sun 10/06/2018 00:56:46
ahahah perfectly coordinated :)

I saw the answer as soon as i edited the post, by the way it seems to work like a charm, thanks Khris. Just for curiosity in wich way you should work with 2d raycast?
Title: Re: Strategic turn based with stealth mechanic - SOLVED
Post by: rmonic79 on Mon 11/06/2018 13:15:23
I'm adding cones like characters that follow enemies directions and positions to check if the player is in line of sight  along with walls check. I tried with isCollidingWithChar but doesn't work cause the check is on baseline i think, are things overlapping works better but alpha channel count as collision, is there an internal function with pixel perfect collision or can you help me maybe in the same function for walls? Thanks.
Title: Re: Strategic turn based with stealth mechanic - SOLVED
Post by: Matti on Mon 11/06/2018 13:30:54
Most recent thread about this issue: http://www.adventuregamestudio.co.uk/forums/index.php?topic=55533

There's a pixel perfect collision module, but it seems to be outdated. Alternatively you can try changing the character's BlockingHeight.
Title: Re: Strategic turn based with stealth mechanic - SOLVED
Post by: Khris on Mon 11/06/2018 16:33:17
It's simpler to do this with math. Afaik you already have a distance check implemented, so all that's left is the angle.
If you have the enemy's line of sight as degree int angle, you can get the normalized vector like this:
  float af = Maths.DegreesToRadians(IntToFloat(angle));
  float ex = Math.Cos(af), ey = Math.Sin(af);

The vector towards the player is again
    float x = IntToFloat(enemy.x), y = IntToFloat(enemy.y);
  float dx = IntToFloat(player.x - enemy.x);
  float dy = IntToFloat(player.y - enemy.y);
  // normalize vector
  float l = Math.sqrt(dx * dx + dy * dy);
  dx /= l;
  dy /= l;

You can now calculate the dot product:
  float dot = dx * ex + dy * ey;
If the two vectors point in the same direction, this product is exactly 1. If they point 90 degrees apart, it's 0. In other words, all you need to check is if (dot > 0.5) and the player is inside the cone. Use a value closer to 1 for a narrower cone.

However I'm guessing you also want to display the cones for the player to see? You can for instance draw them on a semi-transparent, not clickable GUI, again using some Math and DrawTriangle()
Title: Re: Strategic turn based with stealth mechanic - SOLVED
Post by: rmonic79 on Mon 11/06/2018 19:33:06
Sorry Khris but my trigonometric knowledge is old and full of dust. Maybe i forgot to tell that the enemies go around screen (so i had with first function some division by zero if you can help me to avoid them). I can't reproduce the triangle with drawtriangle cause i can't do reverse formula. But it seems that the cone this way is strictly connected with enemy-player distance. there are three player and three enemy by now but i will increase enemy presence.

this is the old ones with things overlapping and cone made with sprite. Just to explain the condition.
(http://i.imgur.com/sjqsNb8l.png) (https://imgur.com/sjqsNb8)
Title: Re: Strategic turn based with stealth mechanic - SOLVED
Post by: Khris on Mon 11/06/2018 19:40:36
The only way to get a division by zero is if the player and enemy have the same coordinates exactly.
After  float l = Math.sqrt(dx * dx + dy * dy);
insert this:  if (l == 0) return true;
Title: Re: Strategic turn based with stealth mechanic - SOLVED
Post by: rmonic79 on Tue 12/06/2018 12:49:13
I think i solved all like i taught using also an old script that i had for npc always look at player in Chronicle of Innsmouth.

Code (ags) Select

function  Angolo(float xgiocatore, float xpersonaggio,  float ygiocatore,  float ypersonaggio)
{
  float x;
  float y;
  float angle;
  float degangle;
  int angolo;
  x= xgiocatore-xpersonaggio;
  y= ygiocatore-ypersonaggio;
  angle = Maths.ArcTan2(x,y);
  degangle=Maths.RadiansToDegrees(angle);
  angolo = FloatToInt(degangle);
  if (angolo<0) angolo=360+angolo;
  return angolo;
}

String  direzione(int angolo)
{
  if ((angolo>=337&&angolo<=360)||(angolo>=0&&angolo<22))
  return "D";
  else if (angolo>=22&&angolo<67)
  return "RD";
  else if (angolo>=67&&angolo<112)
  return "R";
  else if (angolo>=112&&angolo<157)
  return "RU";
  else if (angolo>=157&&angolo<202)
  return "U";
  else if (angolo>=202&&angolo<247)
  return "LU";
  else if (angolo>=247&&angolo<292)
  return "L";
  else if (angolo>=292&&angolo<337)
  return "LD";
 
}

bool SightBlocked(float fx, float fy) {
  int x = FloatToInt(fx, eRoundNearest);
  int y = FloatToInt(fy, eRoundNearest);
  // check if room pixel x, y is blocking
  return GetLocationType(x - GetViewportX(), y - GetViewportY()) == eLocationObject;
}

bool IsVisibleTo(this Character*, Character* enemy) {
  float x = IntToFloat(enemy.x), y = IntToFloat(enemy.y);
  float dx = IntToFloat(this.x - enemy.x);
  float dy = IntToFloat(this.y - enemy.y);
  float l;

   l = Maths.Sqrt(IntToFloat(((this.y - enemy.y)*(this.y - enemy.y))+((this.x - enemy.x)*(this.x - enemy.x))));
  //check every 2 pixels
  if (l == 0.0) return true;
  dx /= l / 2.0;
  dy /= l / 2.0;
  int steps = FloatToInt(l / 2.0, eRoundDown);
   for (int i = 0; i < steps; i++) {
    x += dx; y += dy;
    if (SightBlocked(x, y)) return false;
   }
  return true;
}
void VerificaAngolo(Character *giocatore, Character *nemico){
 
  angolus=Angolo(IntToFloat(giocatore.x), IntToFloat(nemico.x), IntToFloat(giocatore.y), IntToFloat(nemico.y));
  dir=direzione(angolus);
  if (distanza(giocatore.x, giocatore.y, nemico.x, nemico.y)<167)
   {
     if (giocatore.IsVisibleTo(nemico))
    {
        if (nemico.Loop==0)
         {
           
           if (dir=="D")
            {
              giocatore.SayBackground("Mi ha visto.");
            }
            else giocatore.SayBackground("");
         }
         else if (nemico.Loop==1)
            {
              if (dir=="L")
            {
              giocatore.SayBackground("Mi ha visto.");
            }
            else giocatore.SayBackground("");
            }
             else if (nemico.Loop==2)
            {
              if (dir=="R")
            {
              giocatore.SayBackground("Mi ha visto.");
            }
            else giocatore.SayBackground("");
            }
             else if (nemico.Loop==3)
            {
              if (dir=="U")
            {
              giocatore.SayBackground("Mi ha visto.");
            }
            else giocatore.SayBackground("");
            }
    }
   }
}


function repeatedly_execute_always()
{
  VerificaAngolo(cEgo, cGen3);
  VerificaAngolo(cEgo, cGen4);
  VerificaAngolo(cEgo, cGen5);
  VerificaAngolo(cGen1, cGen3);
  VerificaAngolo(cGen1, cGen4);
  VerificaAngolo(cGen1, cGen5);
  VerificaAngolo(cGen2, cGen3);
  VerificaAngolo(cGen2, cGen4);
  VerificaAngolo(cGen2, cGen5);
}