Author Topic: Strategic turn based with stealth mechanic - SOLVED  (Read 273 times)

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.
« Last Edit: 10 Jun 2018, 01:00 by rmonic79 »

Khris

    • Lifetime Achievement Award Winner
    •  
    • I can help with play testing
    •  
    • I can help with scripting
    •  
    • I can help with translating
    •  
    • Khris worked on a game that was nominated for an AGS Award!
Re: Strategic turn based with stealth mechanic
« Reply #1 on: 09 Jun 2018, 11:57 »
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.


Re: Strategic turn based with stealth mechanic
« Reply #3 on: 09 Jun 2018, 14:14 »
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.
« Last Edit: 09 Jun 2018, 14:17 by rmonic79 »

Khris

    • Lifetime Achievement Award Winner
    •  
    • I can help with play testing
    •  
    • I can help with scripting
    •  
    • I can help with translating
    •  
    • Khris worked on a game that was nominated for an AGS Award!
Re: Strategic turn based with stealth mechanic
« Reply #4 on: 09 Jun 2018, 16:10 »
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
Code: Adventure Game Studio
  1. bool SightBlocked(float fx, float fy) {
  2.   int x = FloatToInt(fx, eRoundNearest);
  3.   int y = FloatToInt(fy, eRoundNearest);
  4.   // check if room pixel x, y is blocking
  5.   return GetLocationType(x - GetViewportX(), y - GetViewportY()) == eLocationObject;
  6. }
  7.  
  8. bool IsVisibleTo(this* Character, Character* enemy) {
  9.   float x = IntToFloat(enemy.x), y = IntToFloat(enemy.y);
  10.   float dx = IntToFloat(this.x - enemy.x);
  11.   float dy = IntToFloat(this.y - enemy.y);
  12.   float l = Math.sqrt(dx * dx + dy * dy);
  13.   // check every 2 pixels
  14.   dx /= l / 2.0;
  15.   dy /= l / 2.0;
  16.   int steps = FloatToInt(l / 2.0, eRoundDown);
  17.   for (int i = 0; i < steps; i++) {
  18.     x += dx; y += dy;
  19.     if (SightBlocked(x, y)) return false;
  20.   }
  21.   return true;
  22. }

Now you can use
Code: Adventure Game Studio
  1.   if (player.IsVisibleTo(cPatrol1)) ...
« Last Edit: 10 Jun 2018, 00:41 by Khris »

Re: Strategic turn based with stealth mechanic
« Reply #5 on: 09 Jun 2018, 19:40 »


i had this error

EDIT:
Seems to be solved this way
« Last Edit: 10 Jun 2018, 00:56 by rmonic79 »

Khris

    • Lifetime Achievement Award Winner
    •  
    • I can help with play testing
    •  
    • I can help with scripting
    •  
    • I can help with translating
    •  
    • Khris worked on a game that was nominated for an AGS Award!
Re: Strategic turn based with stealth mechanic
« Reply #6 on: 10 Jun 2018, 00:41 »
Was a typo:

Code: Adventure Game Studio
  1.     float l = Math.sqrt(dx * dx + dy * dy);

Re: Strategic turn based with stealth mechanic
« Reply #7 on: 10 Jun 2018, 00:56 »
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?
« Last Edit: 10 Jun 2018, 01:01 by rmonic79 »

Re: Strategic turn based with stealth mechanic - SOLVED
« Reply #8 on: 11 Jun 2018, 13:15 »
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.

Re: Strategic turn based with stealth mechanic - SOLVED
« Reply #9 on: 11 Jun 2018, 13:30 »
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.

Khris

    • Lifetime Achievement Award Winner
    •  
    • I can help with play testing
    •  
    • I can help with scripting
    •  
    • I can help with translating
    •  
    • Khris worked on a game that was nominated for an AGS Award!
Re: Strategic turn based with stealth mechanic - SOLVED
« Reply #10 on: 11 Jun 2018, 16:33 »
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:
Code: Adventure Game Studio
  1.   float af = Maths.DegreesToRadians(IntToFloat(angle));
  2.   float ex = Math.Cos(af), ey = Math.Sin(af);
The vector towards the player is again
 
Code: Adventure Game Studio
  1.   float x = IntToFloat(enemy.x), y = IntToFloat(enemy.y);
  2.   float dx = IntToFloat(player.x - enemy.x);
  3.   float dy = IntToFloat(player.y - enemy.y);
  4.   // normalize vector
  5.   float l = Math.sqrt(dx * dx + dy * dy);
  6.   dx /= l;
  7.   dy /= l;
You can now calculate the dot product:
Code: Adventure Game Studio
  1.   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()

Re: Strategic turn based with stealth mechanic - SOLVED
« Reply #11 on: 11 Jun 2018, 19:33 »
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.


Khris

    • Lifetime Achievement Award Winner
    •  
    • I can help with play testing
    •  
    • I can help with scripting
    •  
    • I can help with translating
    •  
    • Khris worked on a game that was nominated for an AGS Award!
Re: Strategic turn based with stealth mechanic - SOLVED
« Reply #12 on: 11 Jun 2018, 19:40 »
The only way to get a division by zero is if the player and enemy have the same coordinates exactly.
After
Code: Adventure Game Studio
  1.   float l = Math.sqrt(dx * dx + dy * dy);
insert this:
Code: Adventure Game Studio
  1.   if (l == 0) return true;

Re: Strategic turn based with stealth mechanic - SOLVED
« Reply #13 on: 12 Jun 2018, 12:49 »
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: Adventure Game Studio
  1. function  Angolo(float xgiocatore, float xpersonaggio,  float ygiocatore,  float ypersonaggio)
  2. {
  3.   float x;
  4.   float y;
  5.   float angle;
  6.   float degangle;
  7.   int angolo;
  8.   x= xgiocatore-xpersonaggio;
  9.   y= ygiocatore-ypersonaggio;
  10.   angle = Maths.ArcTan2(x,y);
  11.   degangle=Maths.RadiansToDegrees(angle);
  12.   angolo = FloatToInt(degangle);
  13.   if (angolo<0) angolo=360+angolo;
  14.   return angolo;
  15. }
  16.  
  17.  String  direzione(int angolo)
  18. {
  19.   if ((angolo>=337&&angolo<=360)||(angolo>=0&&angolo<22))
  20.   return "D";
  21.   else if (angolo>=22&&angolo<67)
  22.   return "RD";
  23.   else if (angolo>=67&&angolo<112)
  24.   return "R";
  25.   else if (angolo>=112&&angolo<157)
  26.   return "RU";
  27.   else if (angolo>=157&&angolo<202)
  28.   return "U";
  29.   else if (angolo>=202&&angolo<247)
  30.   return "LU";
  31.   else if (angolo>=247&&angolo<292)
  32.   return "L";
  33.   else if (angolo>=292&&angolo<337)
  34.   return "LD";
  35.  
  36. }
  37.  
  38. bool SightBlocked(float fx, float fy) {
  39.   int x = FloatToInt(fx, eRoundNearest);
  40.   int y = FloatToInt(fy, eRoundNearest);
  41.   // check if room pixel x, y is blocking
  42.   return GetLocationType(x - GetViewportX(), y - GetViewportY()) == eLocationObject;
  43. }
  44.  
  45. bool IsVisibleTo(this Character*, Character* enemy) {
  46.   float x = IntToFloat(enemy.x), y = IntToFloat(enemy.y);
  47.   float dx = IntToFloat(this.x - enemy.x);
  48.   float dy = IntToFloat(this.y - enemy.y);
  49.   float l;
  50.  
  51.    l = Maths.Sqrt(IntToFloat(((this.y - enemy.y)*(this.y - enemy.y))+((this.x - enemy.x)*(this.x - enemy.x))));
  52.   //check every 2 pixels
  53.   if (l == 0.0) return true;
  54.   dx /= l / 2.0;
  55.   dy /= l / 2.0;
  56.   int steps = FloatToInt(l / 2.0, eRoundDown);
  57.    for (int i = 0; i < steps; i++) {
  58.     x += dx; y += dy;
  59.     if (SightBlocked(x, y)) return false;
  60.    }
  61.   return true;
  62. }
  63. void VerificaAngolo(Character *giocatore, Character *nemico){
  64.  
  65.   angolus=Angolo(IntToFloat(giocatore.x), IntToFloat(nemico.x), IntToFloat(giocatore.y), IntToFloat(nemico.y));
  66.   dir=direzione(angolus);
  67.   if (distanza(giocatore.x, giocatore.y, nemico.x, nemico.y)<167)
  68.    {
  69.      if (giocatore.IsVisibleTo(nemico))
  70.     {
  71.         if (nemico.Loop==0)
  72.          {
  73.            
  74.            if (dir=="D")
  75.             {
  76.               giocatore.SayBackground("Mi ha visto.");
  77.             }
  78.             else giocatore.SayBackground("");
  79.          }
  80.          else if (nemico.Loop==1)
  81.             {
  82.               if (dir=="L")
  83.             {
  84.               giocatore.SayBackground("Mi ha visto.");
  85.             }
  86.             else giocatore.SayBackground("");
  87.             }
  88.              else if (nemico.Loop==2)
  89.             {
  90.               if (dir=="R")
  91.             {
  92.               giocatore.SayBackground("Mi ha visto.");
  93.             }
  94.             else giocatore.SayBackground("");
  95.             }
  96.              else if (nemico.Loop==3)
  97.             {
  98.               if (dir=="U")
  99.             {
  100.               giocatore.SayBackground("Mi ha visto.");
  101.             }
  102.             else giocatore.SayBackground("");
  103.             }
  104.     }
  105.    }
  106. }
  107.  
  108.  
  109. function repeatedly_execute_always()
  110. {
  111.   VerificaAngolo(cEgo, cGen3);
  112.   VerificaAngolo(cEgo, cGen4);
  113.   VerificaAngolo(cEgo, cGen5);
  114.   VerificaAngolo(cGen1, cGen3);
  115.   VerificaAngolo(cGen1, cGen4);
  116.   VerificaAngolo(cGen1, cGen5);
  117.   VerificaAngolo(cGen2, cGen3);
  118.   VerificaAngolo(cGen2, cGen4);
  119.   VerificaAngolo(cGen2, cGen5);
  120. }
  121.