Пример #1
0
void worldmap::Generate()
{
  Alloc2D(OldAltitudeBuffer, XSize, YSize);
  Alloc2D(OldTypeBuffer, XSize, YSize);
  Alloc2D(NoIslandAltitudeBuffer, XSize, YSize);

  for(;;)
  {
    RandomizeAltitude();
    SmoothAltitude();
    GenerateClimate();
    SmoothClimate();
    CalculateContinents();
    std::vector<continent*> PerfectForAttnam, PerfectForNewAttnam;

    for(uint c = 1; c < Continent.size(); ++c)
      if(Continent[c]->GetSize() > 250 && Continent[c]->GetSize() < 750
         && Continent[c]->GetGTerrainAmount(EGForestType)
         && Continent[c]->GetGTerrainAmount(SnowType))
        PerfectForAttnam.push_back(Continent[c]);

    if(!PerfectForAttnam.size())
      continue;

    v2 AttnamPos, ElpuriCavePos, NewAttnamPos, TunnelEntry, TunnelExit;
    truth Correct = false;
    continent* PetrusLikes;

    // Store this before we start making islands which have no continent number.
    for(int x = 0; x < XSize; ++x)
      for(int y = 0; y < YSize; ++y)
        NoIslandAltitudeBuffer[x][y] = AltitudeBuffer[x][y];

    for(int c1 = 0; c1 < 25; ++c1)
    {
      game::BusyAnimation();
      PetrusLikes = PerfectForAttnam[RAND() % PerfectForAttnam.size()];
      AttnamPos = PetrusLikes->GetRandomMember(EGForestType);
      ElpuriCavePos = PetrusLikes->GetRandomMember(SnowType);

      for(int c2 = 1; c2 < 50; ++c2)
      {
        TunnelExit = PetrusLikes->GetMember(RAND() % PetrusLikes->GetSize());

        if(AttnamPos != TunnelExit && ElpuriCavePos != TunnelExit)
        {
          for(int d1 = 0; d1 < 8; ++d1)
          {
            v2 Pos = TunnelExit + game::GetMoveVector(d1);

            if(IsValidPos(Pos) && AltitudeBuffer[Pos.X][Pos.Y] <= 0)
            {
              int Distance = 3 + (RAND() & 3);
              truth Error = false;
              TunnelEntry = Pos;

              for(int c2 = 0; c2 < Distance; ++c2)
              {
                TunnelEntry += game::GetMoveVector(d1);

                if(!IsValidPos(TunnelEntry)
                   || AltitudeBuffer[TunnelEntry.X][TunnelEntry.Y] > 0)
                {
                  Error = true;
                  break;
                }
              }

              if(Error)
                continue;

              int x, y;
              int Counter = 0;

              for(x = TunnelEntry.X - 3; x <= TunnelEntry.X + 3; ++x)
              {
                for(y = TunnelEntry.Y - 3; y <= TunnelEntry.Y + 3;
                    ++y, ++Counter)
                  if(Counter != 0 && Counter != 6
                     && Counter != 42 && Counter != 48
                     && (!IsValidPos(x, y)
                         || AltitudeBuffer[x][y] > 0
                         || AltitudeBuffer[x][y] < -350))
                  {
                    Error = true;
                    break;
                  }

                if(Error)
                  break;
              }

              if(Error)
                continue;

              Error = true;

              for(x = 0; x < XSize; ++x)
                if(TypeBuffer[x][TunnelEntry.Y] == JungleType)
                {
                  Error = false;
                  break;
                }

              if(Error)
                continue;

              Counter = 0;

              for(x = TunnelEntry.X - 2; x <= TunnelEntry.X + 2; ++x)
                for(y = TunnelEntry.Y - 2; y <= TunnelEntry.Y + 2;
                    ++y, ++Counter)
                  if(Counter != 0 && Counter != 4
                     && Counter != 20 && Counter != 24)
                    AltitudeBuffer[x][y] /= 2;

              AltitudeBuffer[TunnelEntry.X][TunnelEntry.Y] = 1 + RAND() % 50;
              TypeBuffer[TunnelEntry.X][TunnelEntry.Y] = JungleType;
              GetWSquare(TunnelEntry)->ChangeGWTerrain(jungle::Spawn());
              int NewAttnamIndex;

              for(NewAttnamIndex = RAND() & 7;
                  NewAttnamIndex == 7 - d1;
                  NewAttnamIndex = RAND() & 7);

              NewAttnamPos = TunnelEntry
                             + game::GetMoveVector(NewAttnamIndex);
              static int DiagonalDir[4] = { 0, 2, 5, 7 };
              static int NotDiagonalDir[4] = { 1, 3, 4, 6 };
              static int AdjacentDir[4][2] = { { 0, 1 }, { 0, 2 },
                                               { 1, 3 }, { 2, 3 } };
              truth Raised[] = { false, false, false, false };
              int d2;

              for(d2 = 0; d2 < 4; ++d2)
                if(NotDiagonalDir[d2] != 7 - d1
                   && (NotDiagonalDir[d2] == NewAttnamIndex
                       || !(RAND() & 2)))
                {
                  v2 Pos = TunnelEntry
                           + game::GetMoveVector(NotDiagonalDir[d2]);
                  AltitudeBuffer[Pos.X][Pos.Y] = 1 + RAND() % 50;
                  TypeBuffer[Pos.X][Pos.Y] = JungleType;
                  GetWSquare(Pos)->ChangeGWTerrain(jungle::Spawn());
                  Raised[d2] = true;
                }

              for(d2 = 0; d2 < 4; ++d2)
                if(DiagonalDir[d2] != 7 - d1
                   && (DiagonalDir[d2] == NewAttnamIndex
                       || (Raised[AdjacentDir[d2][0]]
                           && Raised[AdjacentDir[d2][1]] && !(RAND() & 2))))
                {
                  v2 Pos = TunnelEntry
                           + game::GetMoveVector(DiagonalDir[d2]);
                  AltitudeBuffer[Pos.X][Pos.Y] = 1 + RAND() % 50;
                  TypeBuffer[Pos.X][Pos.Y] = JungleType;
                  GetWSquare(Pos)->ChangeGWTerrain(jungle::Spawn());
                }

              Correct = true;
              break;
            }
          }

          if(Correct)
            break;
        }
      }

      if(Correct)
        break;
    }

    if(!Correct)
      continue;

    // Use a Poisson disc sampler to find random nicely-spaced points on the world map
    // Third argument is radius. Radius = 6 produces circa 120 positions (more spaced-out), Radius = 5 produces circa 200 positions (closer together)
    AllocateGlobalPossibleLocations(XSize, YSize, 6, 10);

    // Create a vector of location data structures from the available locations
    std::vector <location> AvailableLocations;

    // Pick out all the locations above ground as valid candidate locations
    for(int x1 = 0; x1 < XSize; ++x1)
      for(int y1 = 0; y1 < YSize; ++y1)
        if((PossibleLocationBuffer[x1][y1] == true) && (NoIslandAltitudeBuffer[x1][y1] > 0))
        {
          AvailableLocations.push_back(location(v2(x1, y1), TypeBuffer[x1][y1], GetContinentUnder(v2(x1, y1))->GetIndex(), (AttnamPos - v2(x1, y1)).GetManhattanLength()));
        }

    // Remove those positions that have already been taken up by core places, plus the origin. Theoretically, New Attnam and Tunnel Entry need not be checked.
    std::vector<v2> ForbiddenPositions = {v2(0, 0), NewAttnamPos, TunnelEntry, TunnelExit, AttnamPos, ElpuriCavePos};
    for(uint i = 0; i < ForbiddenPositions.size(); i++)
    {
      AvailableLocations.erase(
        std::remove_if(
          AvailableLocations.begin(),
          AvailableLocations.end(),
          [ForbiddenPositions, i](location vec) -> truth {return vec.Position == ForbiddenPositions[i];}),
        AvailableLocations.end());
    }

    // Sort the vector of global available locations according to distance to attnam. Closest places are first.
    std::sort(AvailableLocations.begin(), AvailableLocations.end(), distancetoattnam());

    // Make up a vector of places from the script that need to be placed
    std::vector<place> ToBePlaced;
    // Pick out only the places that can be generated, and get their native ground terrain type
    for(int Type = 1; Type < protocontainer<owterrain>::GetSize(); ++Type)
    {
      const owterrain::prototype* Proto = protocontainer<owterrain>::GetProto(Type);
      const owterrain::database*const* ConfigData = Proto->GetConfigData();
      int ConfigSize = Proto->GetConfigSize();

      for(int c = 0; c < ConfigSize; ++c)
      {
        const owterrain::database* DataBase = ConfigData[c];

        if(!DataBase->IsAbstract && DataBase->CanBeGenerated)
        {
          place ConfigID(Type, DataBase->Config, DataBase->NativeGTerrainType, false, DataBase->CanBeOnAnyTerrain);
          ToBePlaced.push_back(ConfigID);
        }
      }
    }

    // Re-order the places so they appear in random order:
    std::random_shuffle(ToBePlaced.begin(), ToBePlaced.end());

    // Do this for as many times as there are number of continents.
    for(uint c = 1; c < Continent.size(); ++c)
    {
      // Get the next nearest continent index by looking at the top of the available locations.
      int ThisContinent = AvailableLocations[0].ContinentIndex;
      
      // Get each available location on the first continent.
      std::vector<location> AvailableLocationsOnThisContinent;
      
      // Collect all remaining available locations on this continent.
      for(uint i = 0; i < AvailableLocations.size(); i++)
      {
        if(AvailableLocations[i].ContinentIndex == ThisContinent)
        {
          AvailableLocationsOnThisContinent.push_back(AvailableLocations[i]);
        }
      }
      // Go through all the locations on the continent. These are always in order of distance to Attnam, closest at the top.
      for(uint i = 0; i < AvailableLocationsOnThisContinent.size(); i++)
      {
        // Go through all remaining places. These are always in a random order :)
        for(uint j = 0; j < ToBePlaced.size(); j++)
        {
          // If the terrain type of the available location matches that of the place, then put the place there.
          if((AvailableLocationsOnThisContinent[i].GTerrainType == GetTypeOfNativeGTerrainType(ToBePlaced[j].NativeGTerrainType)) || (ToBePlaced[j].CanBeOnAnyTerrain))
          {
            owterrain* NewPlace = protocontainer<owterrain>::GetProto(ToBePlaced[j].Type)->Spawn();
            v2 NewPos = AvailableLocationsOnThisContinent[i].Position;

            if(!NewPlace->HideLocationInitially())
              GetWSquare(NewPos)->ChangeOWTerrain(NewPlace);

            SetEntryPos(NewPlace->GetAttachedDungeon(), NewPos);
            ToBePlaced[j].HasBeenPlaced = true;

            if(NewPlace->RevealEnvironmentInitially())
              RevealEnvironment(NewPos, 1);

            break;
          }
        }
        // Remove any places that have been placed, so we don't try to pick them out of our vector again.
        ToBePlaced.erase(
          std::remove_if(
            ToBePlaced.begin(),
            ToBePlaced.end(),
            [](place vec) -> truth {return vec.HasBeenPlaced;}),
          ToBePlaced.end());
      }
      // Remove the locations on the continent we have just examined. Whether or not they populate with places, they will not be re-visited.
      AvailableLocations.erase(
        std::remove_if(
          AvailableLocations.begin(),
          AvailableLocations.end(),
          [ThisContinent](location vec) -> truth {return vec.ContinentIndex == ThisContinent;}),
        AvailableLocations.end());

      AvailableLocationsOnThisContinent.clear();

      // Two early stopping criteria. In the default case we just run out of continents to step through.
      if(AvailableLocations.empty())
        break;

      if(ToBePlaced.empty())
        break;
    }

    GetWSquare(AttnamPos)->ChangeOWTerrain(attnam::Spawn());
    SetEntryPos(ATTNAM, AttnamPos);
    RevealEnvironment(AttnamPos, 1);
    GetWSquare(NewAttnamPos)->ChangeOWTerrain(newattnam::Spawn());
    SetEntryPos(NEW_ATTNAM, NewAttnamPos);
    SetEntryPos(ELPURI_CAVE, ElpuriCavePos);
    GetWSquare(TunnelEntry)->ChangeOWTerrain(underwatertunnel::Spawn());
    SetEntryPos(UNDER_WATER_TUNNEL, TunnelEntry);
    GetWSquare(TunnelExit)->ChangeOWTerrain(underwatertunnelexit::Spawn());
    SetEntryPos(UNDER_WATER_TUNNEL_EXIT, TunnelExit);
    PLAYER->PutTo(NewAttnamPos);
    CalculateLuminances();
    CalculateNeighbourBitmapPoses();
    break;
  }

  delete [] OldAltitudeBuffer;
  delete [] OldTypeBuffer;
  delete [] NoIslandAltitudeBuffer;
}
Пример #2
0
void worldmap::Generate()
{
  Alloc2D(OldAltitudeBuffer, XSize, YSize);
  Alloc2D(OldTypeBuffer, XSize, YSize);

  for(;;)
  {
    RandomizeAltitude();
    SmoothAltitude();
    GenerateClimate();
    SmoothClimate();
    CalculateContinents();
    std::vector<continent*> PerfectForAttnam, PerfectForNewAttnam;

    for(uint c = 1; c < Continent.size(); ++c)
      if(Continent[c]->GetSize() > 25 && Continent[c]->GetSize() < 1000
	 && Continent[c]->GetGTerrainAmount(EGForestType)
	 && Continent[c]->GetGTerrainAmount(SnowType))
	PerfectForAttnam.push_back(Continent[c]);

    if(!PerfectForAttnam.size())
      continue;

    v2 AttnamPos, ElpuriCavePos, NewAttnamPos, TunnelEntry, TunnelExit;
    truth Correct = false;
    continent* PetrusLikes;

    for(int c1 = 0; c1 < 25; ++c1)
    {
      game::BusyAnimation();
      PetrusLikes = PerfectForAttnam[RAND() % PerfectForAttnam.size()];
      AttnamPos = PetrusLikes->GetRandomMember(EGForestType);
      ElpuriCavePos = PetrusLikes->GetRandomMember(SnowType);

      for(int c2 = 1; c2 < 50; ++c2)
      {
	TunnelExit = PetrusLikes->GetMember(RAND() % PetrusLikes->GetSize());

	if(AttnamPos != TunnelExit && ElpuriCavePos != TunnelExit)
	{
	  for(int d1 = 0; d1 < 8; ++d1)
	  {
	    v2 Pos = TunnelExit + game::GetMoveVector(d1);

	    if(IsValidPos(Pos) && AltitudeBuffer[Pos.X][Pos.Y] <= 0)
	    {
	      int Distance = 3 + (RAND() & 3);
	      truth Error = false;
	      TunnelEntry = Pos;

	      for(int c2 = 0; c2 < Distance; ++c2)
	      {
		TunnelEntry += game::GetMoveVector(d1);

		if(!IsValidPos(TunnelEntry)
		   || AltitudeBuffer[TunnelEntry.X][TunnelEntry.Y] > 0)
		{
		  Error = true;
		  break;
		}
	      }

	      if(Error)
		continue;

	      int x, y;
	      int Counter = 0;

	      for(x = TunnelEntry.X - 3; x <= TunnelEntry.X + 3; ++x)
	      {
		for(y = TunnelEntry.Y - 3; y <= TunnelEntry.Y + 3;
		    ++y, ++Counter)
		  if(Counter != 0 && Counter != 6
		     && Counter != 42 && Counter != 48
		     && (!IsValidPos(x, y)
			 || AltitudeBuffer[x][y] > 0
			 || AltitudeBuffer[x][y] < -350))
		  {
		    Error = true;
		    break;
		  }

		if(Error)
		  break;
	      }

	      if(Error)
		continue;

	      Error = true;

	      for(x = 0; x < XSize; ++x)
		if(TypeBuffer[x][TunnelEntry.Y] == JungleType)
		{
		  Error = false;
		  break;
		}

	      if(Error)
		continue;

	      Counter = 0;

	      for(x = TunnelEntry.X - 2; x <= TunnelEntry.X + 2; ++x)
		for(y = TunnelEntry.Y - 2; y <= TunnelEntry.Y + 2;
		    ++y, ++Counter)
		  if(Counter != 0 && Counter != 4
		     && Counter != 20 && Counter != 24)
		    AltitudeBuffer[x][y] /= 2;

	      AltitudeBuffer[TunnelEntry.X][TunnelEntry.Y] = 1 + RAND() % 50;
	      TypeBuffer[TunnelEntry.X][TunnelEntry.Y] = JungleType;
	      GetWSquare(TunnelEntry)->ChangeGWTerrain(jungle::Spawn());
	      int NewAttnamIndex;

	      for(NewAttnamIndex = RAND() & 7;
		  NewAttnamIndex == 7 - d1;
		  NewAttnamIndex = RAND() & 7);

	      NewAttnamPos = TunnelEntry
			     + game::GetMoveVector(NewAttnamIndex);
	      static int DiagonalDir[4] = { 0, 2, 5, 7 };
	      static int NotDiagonalDir[4] = { 1, 3, 4, 6 };
	      static int AdjacentDir[4][2] = { { 0, 1 }, { 0, 2 },
					       { 1, 3 }, { 2, 3 } };
	      truth Raised[] = { false, false, false, false };
	      int d2;

	      for(d2 = 0; d2 < 4; ++d2)
		if(NotDiagonalDir[d2] != 7 - d1
		   && (NotDiagonalDir[d2] == NewAttnamIndex
		       || !(RAND() & 2)))
		{
		  v2 Pos = TunnelEntry
			   + game::GetMoveVector(NotDiagonalDir[d2]);
		  AltitudeBuffer[Pos.X][Pos.Y] = 1 + RAND() % 50;
		  TypeBuffer[Pos.X][Pos.Y] = JungleType;
		  GetWSquare(Pos)->ChangeGWTerrain(jungle::Spawn());
		  Raised[d2] = true;
		}

	      for(d2 = 0; d2 < 4; ++d2)
		if(DiagonalDir[d2] != 7 - d1
		   && (DiagonalDir[d2] == NewAttnamIndex
		       || (Raised[AdjacentDir[d2][0]]
			   && Raised[AdjacentDir[d2][1]] && !(RAND() & 2))))
		{
		  v2 Pos = TunnelEntry
			   + game::GetMoveVector(DiagonalDir[d2]);
		  AltitudeBuffer[Pos.X][Pos.Y] = 1 + RAND() % 50;
		  TypeBuffer[Pos.X][Pos.Y] = JungleType;
		  GetWSquare(Pos)->ChangeGWTerrain(jungle::Spawn());
		}

	      Correct = true;
	      break;
	    }
	  }

	  if(Correct)
	    break;
	}
      }

      if(Correct)
	break;
    }

    if(!Correct)
      continue;

    GetWSquare(AttnamPos)->ChangeOWTerrain(attnam::Spawn());
    SetEntryPos(ATTNAM, AttnamPos);
    RevealEnvironment(AttnamPos, 1);
    GetWSquare(NewAttnamPos)->ChangeOWTerrain(newattnam::Spawn());
    SetEntryPos(NEW_ATTNAM, NewAttnamPos);
    SetEntryPos(ELPURI_CAVE, ElpuriCavePos);
    GetWSquare(TunnelEntry)->ChangeOWTerrain(underwatertunnel::Spawn());
    SetEntryPos(UNDER_WATER_TUNNEL, TunnelEntry);
    GetWSquare(TunnelExit)->ChangeOWTerrain(underwatertunnelexit::Spawn());
    SetEntryPos(UNDER_WATER_TUNNEL_EXIT, TunnelExit);
    PLAYER->PutTo(NewAttnamPos);
    CalculateLuminances();
    CalculateNeighbourBitmapPoses();
    break;
  }

  delete [] OldAltitudeBuffer;
  delete [] OldTypeBuffer;
}