Generating a maze?

Started by FrankT, Fri 26/10/2012 14:02:55

Previous topic - Next topic

FrankT

Now I think we're talking major programming here! You know that maze in LSL3? Quoting Al Lowe:

QuoteThere was a 72-screen maze in that game where you got lost in a bamboo grove. The entire maze was made up of one background picture of intersecting paths in a bamboo grove that exited out all four sides of the picture. We also made 4 cells that were mostly transparent, but that contained enough bamboo to completely cover each of those exits. We overlaid them onto that picture. For example, if there was an exit to the top, we didn't add that overlay, thus you could exit out the top. So usually we added two, or sometimes only one, overlay.

But why didn't the scenes all look identical then since every picture was the same? Simple. Every time you exited one screen, we flopped everything horizontally and thus projected the picture backwards, like a slide reversed in a tray. By eliminating disk access, this technique made everything work really fast, took up little memory, and only a tiny bit of disk space.

Presumably, something like that is possible in AGS; has anyone done it before? How does the script work?

Snarky

#1
Well, which part of it?

Overlaying different sprites to modify/hide part of the background can be done with objects, or by rawdrawing. To flip a background, use DynamicSprite.Flip. (You can create the DynamicSprite with DynamicSprite.CreateFromBackground, or just store the background as a sprite in the first place.) The logic of the whole thing (knowing which screen you're in, which sprites should be drawn and which walkable areas/hotspots should be active, what side the player should enter from, etc.) is just coding.

The concerns Lowe had with disk access and storage aren't really relevant any more, but it can still be useful to generate collections of rooms that all look much the same (e.g. maze screens) in-engine from a base template with various overlays, simply to save work on the graphics side. And of course you'd do it all as a single AGS room that you just "dress" differently each time the player "exits" and "enters" it.

But none of this has anything to do with actually generating the maze in the first place. If you want a randomly generated labyrinth, there are algorithms for that.

Slasher

#2
Similar could be done with AGS.

I have never done it the way of Larry Laffer but I do implement a cave Labyrinth in 'Elf: And soon the darkness...' which is similar but not quite the same and only uses 2 backgrounds.

Of course 72 rooms should be much reduced though but could be used.

Some flipped backgrounds would add variety to the rooms look, naturally.

Creating exits would again be fairly easy to implement.

Once you have the maze mapped out (paper / computer) and the exits to each room decided then you would draw exit images to add to where the exits are.

Something along these lines I would say.... But there may well be other ways better suited. It's down to personal choice.

Of course scripting is another thing, but again should not be too difficult.

Good luck



Crimson Wizard

Right now I am helping other people with their game, and there we have a desert "maze" made of 8 rooms (because they required different backgrounds/walkable areas), but those 8 rooms are used to simulate 8x6 area.

In most simple case you construct an array of relations between maze's locations and actual rooms (you may do that by manually setting each element of an array, or writing a map editor utility, or use any kind of random generation).
In my case I used two arrays: LocationToRoom (to know which room should represent each location) and LocationLinks (to know the neighbouring locations accessible from every other location).

Here's some of my code you may use for reference, if you wish:

1. The maze module header
Code: ags

// Struct here serves no other purpose but to group and mark out functions,
// related to current module's task.
struct DesertMap
{
  import static function EnterDesertLocation(int location, RoomTravelDirection enterFrom);
  import static function LeaveDesertLocation(RoomTravelDirection whereTo);
  import static bool CanLeaveTo(RoomTravelDirection whereTo);
  
  import static int WhereAt();
  import static int WhereAtX();
  import static int WhereAtY();
  import static RoomTravelDirection WhereCameFrom();
};


2. The maze module source
Code: ags


static int DesertMap::WhereAt()
{
  return LocationId;
}

static int DesertMap::WhereAtX()
{
  return LocationId % DESERT_WIDTH;
}

static int DesertMap::WhereAtY()
{
  return LocationId / DESERT_WIDTH;
}

static RoomTravelDirection DesertMap::WhereCameFrom()
{
  return WhereFrom;
}

static function DesertMap::EnterDesertLocation(int location, RoomTravelDirection whereFrom)
{
  PrevLocationId = LocationId;
  LocationId = location;
  WhereFrom = whereFrom;
  
  int x, y;
  if (whereFrom == eRoomTravelLeft) { x = -20; y = System.ViewportWidth / 2; }
  else if (whereFrom == eRoomTravelRight) { x = System.ViewportWidth + 20; y = System.ViewportHeight / 2; }
  else if (whereFrom == eRoomTravelUp) { x = System.ViewportWidth / 2; y = -20; }
  else if (whereFrom == eRoomTravelDown) { x = System.ViewportWidth; y = System.ViewportHeight + 20; }
  else { x = System.ViewportWidth / 2; y = System.ViewportHeight / 2; }
  
  player.ChangeRoom(LocationToRoom[location], x, y);
}

static bool DesertMap::CanLeaveTo(RoomTravelDirection whereTo)
{
  if (LocationId < 0 || LocationId >= DESERT_LOCATIONS_NUMBER)
  {
    Utils.GameLogicError("DesertMap::LeaveDesertLocation : [LocationId is out of allowed range.");
    return false;
  }
  
  if (LocationLinks[LocationId * 4 + whereTo] < 0)
  {
    // Can't go there
    return false;
  }
  
  return true;
}

static function DesertMap::LeaveDesertLocation(RoomTravelDirection whereTo)
{
  int nondesert_location;
  
  if (!DesertMap.CanLeaveTo(whereTo))
  {
    return;
  }
  
  PrevLocationId = LocationId;
  WhereFrom = Utils.GetOppositeTravelDirection(whereTo);
  
  if (LocationLinks[LocationId * 4 + whereTo] >= DESERT_LOCATIONS_NUMBER)
  {
    // Changing to non-desert location
    nondesert_location = LocationLinks[LocationId * 4 + whereTo] - DESERT_LOCATIONS_NUMBER;
    LocationId = -1;
  }
  else
  {
    LocationId = LocationLinks[LocationId * 4 + whereTo];
  }
  
  int x, y;
  if (WhereFrom == eRoomTravelLeft) { x = -20; y = System.ViewportWidth / 2; }
  else if (WhereFrom == eRoomTravelRight) { x = System.ViewportWidth + 20; y = System.ViewportHeight / 2; }
  else if (WhereFrom == eRoomTravelUp) { x = System.ViewportWidth / 2; y = -20; }
  else if (WhereFrom == eRoomTravelDown) { x = System.ViewportWidth; y = System.ViewportHeight + 20; }
  else { x = System.ViewportWidth / 2; y = System.ViewportHeight / 2; }

  if (LocationId >= 0)
  {
    //Display("Change from %d to %d (real room %d)", PrevLocationId, LocationId, LocationToRoom[LocationId]);
    player.ChangeRoom(LocationToRoom[LocationId], x, y);
  }
  else
  {
    //Display("Change from %d to real room %d", PrevLocationId, nondesert_location);
    player.ChangeRoom(nondesert_location, x, y);
  }
}


3. Finally, at the each of the 8 "desert" rooms I have the similar code (sometimes with certain modifications):
Code: ags

function room_LeaveBottom()
{
  DesertMap.LeaveDesertLocation(eRoomTravelDown);
}

function room_LeaveLeft()
{
  DesertMap.LeaveDesertLocation(eRoomTravelLeft);
}

function room_LeaveRight()
{
  DesertMap.LeaveDesertLocation(eRoomTravelRight);
}

function room_LeaveTop()
{
  DesertMap.LeaveDesertLocation(eRoomTravelUp);
}

SMF spam blocked by CleanTalk