Mapping a Network of Locations [SOLVED]

Started by Atelier, Sat 22/10/2011 15:13:32

Previous topic - Next topic

Atelier

I need help again ;)

Each location in my game can have four exits... north east south and west.

Code: ags

loc[1].nor = 2;
loc[2].sou = 1;
loc[3].eas = 4;
loc[4].wes = 3;


In this fashion they are all interlinked.

Now, say I want to generate a physical map of the location network. I need some kind of formula to position components on a drawing surface. The result would look something like this:



As an aside, the generated map doesn't need long line connectors, only short ones like between the green star and tavern. Here's what I've come up with so far, but it's not ideal:

Code: ags

//Map Components
   //Squares
         //'you are here' square - 12x12, slot 10
         //plain square - 12x12, slot 7
         //tavern square...
         //stable square... 
   //Connectors
         //1 vertical line - 4x6, slot 8
         //1 horizontal line - 6x4, slot 9



DynamicSprite *sprite;
DrawingSurface *surface;

function GenerateMap()
{
      sprite = DynamicSprite.CreateFromExistingSprite(Map.Graphic);    //Map.Graphic is 162x112, and invisible
      surface = sprite.GetDrawingSurface(); surface.Clear();

      int x = (Map.Width/2)-6,    //find the centre point and -6, because 1 square is 12x12 so it must span the centre
          y = (Map.Height/2)-6;

      surface.DrawImage(x, y, 10);  //mob[0] is the player. draw their location.

            if (loc[mob[0].loc].nor != 0) {    //ie, there is an exit to the north
                  surface.DrawImage(x, y-18, 7); //draw a plain square
                  surface.DrawImage(x+4, y-6, 9); //draw a line connecting the two
            }

            if (loc[mob[0].loc].eas != 0) {   
                  surface.DrawImage(x+18, y, 7);
                  surface.DrawImage(x+12, y+4, 8);
            }

            if (loc[mob[0].loc].sou != 0) {  
                  surface.DrawImage(x, y+18, 7);
                  surface.DrawImage(x+4, y+12, 9);
            }

            if (loc[mob[0].loc].wes != 0) {  
                  surface.DrawImage(x-18, y, 7);
                  surface.DrawImage(x-6, y+4, 8);
            }

   surface.Release();
   Map.NormalGraphic = sprite.Graphic;
}


As I say, it's pretty useless because it only draws the direct satellite locations of the current location. What I need to do is get the satellite locations of the satellite locations of the satellite locations... etc. In this way I create a map of the network, with the current location in the centre.

I would need some kind of concatanating sequence:

Code: ags

if (loc[mob[0].loc].nor != 0) //draw
if (loc[loc[mob[0].loc].nor].nor != 0) //draw
if (loc[loc[loc[mob[0].loc].nor].nor].nor != 0) //draw


But this would be ridiculous, especially as there are 100s of locations. Any ideas for alternatives? :/

pcj

Sounds like you need a loop rather than appending more iterations of the array.  Continue through the loop until there are no more exits.

Also, pretty sure this isn't a beginner question.
Space Quest: Vohaul Strikes Back is now available to download!

Calin Leafshade

#2
This is essentially a version of A* pathfinding.

You basically add nodes to a list and iterate through the list, removing checked nodes until you are left with nodes that have no exits.

You *will* need long connectors because your locations are not stored on a grid (if they are, then that would make life much easier because you can just use a 2D array) If you dont use long connectors then you will get clashes between adjacent compass points like this:


  X - - A - - X
  |     |
  B - - * 



The node at the star is both south of A and east of B which causes a conflict.


Khris

I'd use recursion. In other words, drawing one location includes drawing all adjacent locations except the one that called it.

pseudo
Code: ags
void draw(int location, int skip_location)

  if (location isnt_drawn) {
    draw location;
    mark location as drawn;
    loop through adjacent_locations {
      if (adjacent_location != skip_location) draw(adjacent_location, location);
    }
  }
}


This should draw all locations connected to the current one.

Calin Leafshade

AGS has a limit on the call stack though and i dont think its very large.

Atelier

Quote from: Calin Leafshade on Sat 22/10/2011 16:33:27
The node at the star is both south of A and east of B which causes a conflict.

I have multiple instances of that. Many locations are connected to two or more locations. Does this mean the solution is easier or harder?

Quote from: pcj on Sat 22/10/2011 16:06:45Also, pretty sure this isn't a beginner question.

I forget I'm not a beginner anymore :P

Atelier

#6
I managed to do it in the end, by assigning locations an x and y coordinate. I'll post the code for anyone who finds it useful.

Code: ags

DynamicSprite *sprite;
DrawingSurface *surface;

function GenerateMap()
{
      sprite = DynamicSprite.CreateFromExistingSprite(Map.Graphic);
      surface = sprite.GetDrawingSurface(); surface.Clear();

      int x = (Map.Width/2)-6,
          y = (Map.Height/2)-6,
          i = 1, square;

      while (i < Max_Locs) {

            if (loc[i].name != null) {  //ie, room has been defined
            int xdraw, ydraw;

            if (loc[i].x < loc[mob[o].loc].x) xdraw = x-(loc[mob[0].loc].x-loc[i].x)*19;
            else if (loc[mob[0].loc].x < loc[i].x) xdraw = x+(loc[i].x-loc[mob[0].loc].x)*19;
            else if (loc[mob[0].loc].x == loc[i].x) xdraw = x;

            if (loc[i].y < loc[mob[0].loc].y) ydraw = y+(loc[mob[0].loc].y-loc[i].y)*19;
            else if (loc[mob[o].loc].y < loc[i].y) ydraw = y-(loc[i].y-loc[mob[0].loc].y)*19;
            else if (loc[mob[0].loc].y == loc[i].y) ydraw = y;

            if (loc[i].type == eLocTavern) square == 11;
            else if (loc[i].type == eLocTravel) square == 12;
            // ...
            else square = 7;

            surface.DrawImage(xdraw, ydraw, square);
            if (loc[i].nor) surface.DrawImage(xdraw+4, ydraw-6, 9);
            if (loc[i].eas) surface.DrawImage(xdraw+13, ydraw+4, 8);
            if (loc[i].sou) surface.DrawImage(xdraw+4, ydraw+13, 9);
            if (loc[i].wes) surface.DrawImage(xdraw-6, ydraw+4, 8);

            }
      i++;
      }

      surface.DrawPixel(x+6, y+6);
      surface.Release();

      // Save the image to a file
      //sprite.Resize(500, 500);
      //sprite.SaveToFile("mapoverview.bmp");

      Map.NormalGraphic = sprite.Graphic;
}


Here's just a small test map it generated. Lots of potential!



Khris

A small suggestion regarding this part:

Code: ags
            if (loc[i].type == eLocTavern) square == 11;
            else if (loc[i].type == eLocTravel) square == 12;
            // ...
            else square = 7;


enums are handled as ints, you can even redefine the values. By default, the first enum member is 1, the second is 2 etc.
So you should be able to do:
            square = loc[i].type + 10;
(Assuming that the sprites are ordered in the same way as the enum members.)

SMF spam blocked by CleanTalk