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; }
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; }