private void WorldMapBox_MouseDown(object sender, MouseEventArgs e) { int x = (worldRenderer.Position.X + e.X) / (20 + zoom); int y = (worldRenderer.Position.Y + e.Y) / (20 + zoom); WeatherPoint wP = World.Instance.atmosphere[x * World.MaxZ / World.RATIO + y / World.RATIO * World.MaxZ * World.WORLD_SIZE / World.RATIO]; WorldTile point = World.Instance.worldMap[x, y]; pointInformation.Text = $"Co-ords: {x} {y} Temp: {wP.temperature} Pressure: {wP.pressure} Humidity: {wP.humidity} Lon Wind {wP.v} Lat Wind {wP.u} " + $"Height {point.height} Water: {point.rock.WaterAmount} Average Temp: {point.averageTemp} Landwater {point.landWater}"; if (e.Button == MouseButtons.Left) { mouseDownPosition = e.Location; } }
public void DetermineBiome() { WorldTileType oldType = type; //Determine average averageTemp and water double aTemp = 0; double aWater = 0; int scanSize = 3; for (int y = 0; y < scanSize; y++) { for (int x = 0; x < scanSize; x++) { int lX = (position.X - (x - scanSize / 2)).Cut(0, WORLD_SIZE - 1); int lY = (position.Y - (y - scanSize / 2)).Cut(0, WORLD_SIZE - 1); aTemp += Instance.worldMap[lX, lY].averageTemp; aWater += Instance.worldMap[lX, lY].landWater; } } aTemp /= scanSize * scanSize; aWater /= scanSize * scanSize; if (height < 0.42) { if (aTemp < 0) { type = WorldTileType.SeaIce; } else { type = WorldTileType.Ocean; } Instance.costMap[position.X, position.Y] = 20; } else if (height > 0.7) { type = WorldTileType.Alpine; Instance.costMap[position.X, position.Y] = 4; } else if (aTemp < 5) { type = WorldTileType.Tundra; Instance.costMap[position.X, position.Y] = 2; } else if (aWater > 40 && aTemp > 15) { type = WorldTileType.Rainforest; Instance.costMap[position.X, position.Y] = 4; } else if (aWater < 15 && aTemp > 20) { type = WorldTileType.Desert; Instance.costMap[position.X, position.Y] = 3; } else if (aTemp > 18) { type = WorldTileType.Savanna; Instance.costMap[position.X, position.Y] = 2; } else if (aWater > 20) { type = WorldTileType.TemperateForest; Instance.costMap[position.X, position.Y] = 1; } else { type = WorldTileType.TemperateGrassland; Instance.costMap[position.X, position.Y] = 1; } if (type != oldType) { Instance.lost[oldType]++; Instance.gained[type]++; WeatherPoint weatherPoint = Instance.GetAtmosphereValue(position.X, position.Y, 0); weatherPoint.baseEvaporationRate = GetEvaporationRate(type); weatherPoint.albedo = GetAlebdo(type); weatherPoint.groundHeatCapacity = GetGroundHeatCapacity(type); weatherPoint.baseMinimumHumditiy = GetMinimumHumdity(type); } landWater = 0; }
private void CalculateWeather(int startY, int endY) { //Create a new random instance as the global one is not thread safe Random random = new Random(startY + endY); //We simply calculate each one from the values around them, so they will have old and new value used for (int x = 0; x < WORLD_SIZE / RATIO; x++) { for (int y = startY / RATIO; y < endY / RATIO; y++) { for (int z = 0; z < MaxZ / DZ; z++) { int position = z + MaxZ * x + y * WORLD_SIZE / RATIO * MaxZ; WeatherPoint point = atmosphere[position]; point.hour += atmosphereTimeStep; // Horizontal wind WeatherPoint right; if (x == WORLD_SIZE / RATIO - 1) { right = atmosphere[z + y * WORLD_SIZE / RATIO * MaxZ]; } else { right = atmosphere[z + MaxZ * (x + 1) + y * WORLD_SIZE / RATIO * MaxZ]; } WeatherPoint left; if (x == 0) { left = atmosphere[z + MaxZ * (WORLD_SIZE / RATIO - 1) + y * WORLD_SIZE / RATIO * MaxZ]; } else { left = atmosphere[z + MaxZ * (x - 1) + y * WORLD_SIZE / RATIO * MaxZ]; } float moveU = (point.u * atmosphereTimeStep * 5) / (DX * RATIO); right.u += moveU / 2; left.u += moveU / 2; point.u -= moveU; //float friction = point.u * 0.05 * atmosphereTimeStep * point.mountain; //Go from high pressure to low pressure float pressureDiff = -(1 - point.Resistance) * WindStrength * 10 * (right.pressure - left.pressure) / (DX * RATIO); float coriolis = atmosphereTimeStep * point.f; float dU = coriolis + pressureDiff;// - friction; if (Math.Sign(dU) == Math.Sign(point.u)) { dU = Math.Sign(dU) * Math.Max(2 * Math.Abs(dU) - Math.Abs(point.u), 0); } point.u += dU; point.u = point.u.Cut(-MaxWindStrength, MaxWindStrength); // Vertical wind WeatherPoint bottom; if (y != WORLD_SIZE / RATIO - 1) { bottom = atmosphere[z + MaxZ * x + (y + 1) * WORLD_SIZE / RATIO * MaxZ]; } else { bottom = point; } WeatherPoint top; if (y != 0) { top = atmosphere[z + MaxZ * x + (y - 1) * WORLD_SIZE / RATIO * MaxZ]; } else { top = point; } float moveV = (point.v * atmosphereTimeStep * 20) / (DY * RATIO); top.v += moveV / 4 * 3; bottom.v += moveV / 4; point.v -= moveV; //float friction = point.v * 0.05f * atmosphereTimeStep * point.mountain; float dV = -(1 - point.Resistance) * WindStrength * (top.pressure - bottom.pressure) / (DY * RATIO); // - friction; if (Math.Sign(dV) == Math.Sign(point.v)) { dV = Math.Sign(dV) * Math.Max(5 * Math.Abs(dV) - Math.Abs(point.v), 0); } point.v += dV; point.v = point.v.Cut(-MaxWindStrength, MaxWindStrength); // Pressure float pressure = point.pressure; point.pressure = 1000 + point.temperature * 1.8f + point.dT * 10;// + point.humidity / 10; point.dP = pressure - point.pressure; // Vertical wind - as long as MaxZ is 1 this is not important // float dW = point.dP * (-dU / DX - dV / DY); // point.w += dW; // Temperature //TODO: Calculate the effect of the wind float radiation = point.EnergyBalance * atmosphereTimeStep / (40f * point.groundHeatCapacity); float latitudinalTemp = 20 * atmosphereTimeStep * ((left.temperature + right.temperature) / 2 - point.temperature) / (DX * (RATIO / 2).Cut(1, 10)); float longitudinalTemp = 20 * atmosphereTimeStep * ((top.temperature + bottom.temperature) / 2 - point.temperature) / (DY * (RATIO / 2).Cut(1, 10)); WeatherPoint below; if (z != 0) { below = atmosphere[(z - 1) + MaxZ * x + y * WORLD_SIZE / RATIO * MaxZ]; } else { below = point; } WeatherPoint above; if (z != MaxZ - 1) { above = atmosphere[(z + 1) + MaxZ * x + y * WORLD_SIZE / RATIO * MaxZ]; } else { above = point; } float verticalTemp = atmosphereTimeStep * (below.temperature - above.temperature) / (2 * DZ); point.dT = radiation + point.windTemperatureChange + latitudinalTemp + longitudinalTemp + verticalTemp; point.windTemperatureChange = 0; point.temperature += point.dT; point.temperature = point.temperature.Cut(-100, 100); point.temperature = (float)FastMath.Round(point.temperature, 3); // Calculate humidity // First add new float dH = point.EvaporationRate * atmosphereTimeStep * (1 - point.CloudCover / 100) / 4; point.humidity += dH; //point.humidity = point.humidity.Cut(0, 100); //Maybe not necessary anymore //Change in humidity also affects temperature float dTfromdH = -dH / 10; point.temperature += dTfromdH; point.dT += dTfromdH; //Movement due to wind int xMovement = (int)point.u / 10; int yMovement = (int)point.v / 10; int gX = x + xMovement; while (0 > gX) { gX += WORLD_SIZE / RATIO; } while (gX >= WORLD_SIZE / RATIO) { gX -= WORLD_SIZE / RATIO - 1; } int gY = y + yMovement; while (0 > gY) { gY += WORLD_SIZE / RATIO; } while (gY >= WORLD_SIZE / RATIO) { gY -= WORLD_SIZE / RATIO - 1; } WeatherPoint moveTo = atmosphere[z + gX * MaxZ + gY * MaxZ * WORLD_SIZE / RATIO]; //Move humidity float proportion = Math.Min(2, Math.Abs(atmosphereTimeStep * (Math.Abs(point.u) + Math.Abs(point.v)) / (DX * RATIO))) / 2f; float changed = (float)FastMath.Round((point.humidity - point.movedHumidity - 1).Cut(0, 100) * proportion, 3); point.movedHumidity = 0; if (moveTo.humidity < point.humidity * 2) { point.humidity -= changed; if (point.humidity < 0) { throw new Exception(); } moveTo.humidity += changed; moveTo.movedHumidity += changed; } //Move temperature //TODO: Find way to incorporate wind speed float diff = point.temperature - (moveTo.temperature + moveTo.windTemperatureChange); moveTo.windTemperatureChange += diff * 0.05f; //Now rain point.precipitation = 0; if (point.humidity > point.MinimumHumditiy) { if (random.Next(3) == 0) { point.Raining = true; } } if (point.Raining) { float delta = random.Next(Math.Min(20, (int)point.humidity)); if (delta > 5) { point.precipitation += delta; point.humidity -= delta / 2; if (point.humidity < 0) { throw new Exception(); } for (int X = x * RATIO; X < ((x + 1) * RATIO).Cut(0, WORLD_SIZE - 1); X++) { for (int Y = y * RATIO; Y < ((y + 1) * RATIO).Cut(0, WORLD_SIZE - 1); Y++) { if (IsLand(worldMap[X, Y].type)) { worldMap[X, Y].rock.WaterAmount += delta * 0.3; } } } dTfromdH = -delta / 10; point.temperature += dTfromdH; point.dT += dTfromdH; } if (point.humidity < 10) { point.Raining = false; } } var tile = worldMap[x, y]; tile.averageTemp = (tile.averageTemp * tile.measurements + point.temperature) / (tile.measurements + 1); tile.measurements++; //totalHumidity += point.humidity; #if DEBUG if (point.humidity < 0) { throw new Exception(); } if (CheckValdidity(point.EnergyBalance)) { throw new Exception(); } if (CheckValdidity(point.temperature) || new Range(-150, 100).Excludes(point.temperature)) { throw new Exception(); } if (CheckValdidity(point.dT)) { throw new Exception(); } if (CheckValdidity(point.pressure)) { throw new Exception(); } if (CheckValdidity(point.dP)) { throw new Exception(); } if (CheckValdidity(point.humidity)) { throw new Exception(); } if (CheckValdidity(point.CloudCover)) { throw new Exception(); } if (CheckValdidity(point.u)) { throw new Exception(); } if (CheckValdidity(point.v)) { throw new Exception(); } if (CheckValdidity(point.w)) { throw new Exception(); } #endif } } } }
//double totalHumidity = 0; /// <summary> /// /// </summary> /// <param name="ratio"></param> public void InitialiseAtmosphere(int ratio = 1) { lost = new Dictionary <WorldTileType, int>(); gained = new Dictionary <WorldTileType, int>(); ResetBiomeChangeData(); DateTime time = DateTime.Now; WeatherPoint[] oldAtmosphere = atmosphere; if (oldAtmosphere[0] is null) { atmosphere = new WeatherPoint[WORLD_SIZE / ratio * WORLD_SIZE / ratio * MaxZ / DZ]; //Create a new atmosphere, x and y are coords in atmosphere not ground coords for (int x = 0; x < WORLD_SIZE / ratio; x++) { for (int y = 0; y < WORLD_SIZE / ratio; y++) { for (int z = 0; z < MaxZ / DZ; z++) { //TODO: Get a correct temperature decrease with height float evaporationRate = z == 0 ? (float)worldMap.GetAverage(x * ratio, (x + 1) * ratio, y * ratio, (y + 1) * ratio, t => GetEvaporationRate(t.type)) : 0; float minimumHumdity = z == 0 ? (float)worldMap.GetAverage(x * ratio, (x + 1) * ratio, y * ratio, (y + 1) * ratio, t => GetMinimumHumdity(t.type)) : 0; float groundHeatCapacity = z == 0 ? (float)worldMap.GetAverage(x * ratio, (x + 1) * ratio, y * ratio, (y + 1) * ratio, t => GetGroundHeatCapacity(t.type)) : 0; float albedo = (float)worldMap.GetAverage(x * ratio, (x + 1) * ratio, y * ratio, (y + 1) * ratio, t => GetAlebdo(t.type)); float height = (float)heightMap.GetAverage(x * ratio, (x + 1) * ratio, y * ratio, (y + 1) * ratio, h => h < 0.43 ? 0.43 : h); float latitude = (y * ratio - WORLD_SIZE / 2) / ((float)WORLD_SIZE / 2) * 180; atmosphere[z + x * MaxZ + y * WORLD_SIZE / ratio * MaxZ] = new WeatherPoint(0, 0, 0, (float)temperatureMap[x, y] * 35 - z, z, latitude, x * ratio, y * ratio, height, albedo, evaporationRate, minimumHumdity, (x * ratio - WORLD_SIZE / 2f) / (WORLD_SIZE / 2f) * 180f, groundHeatCapacity); } } } } else { //Scale the data from the old one if (RATIO > ratio) { //The old one was less precise atmosphere = new WeatherPoint[WORLD_SIZE / ratio * WORLD_SIZE / ratio * MaxZ / DZ]; for (int x = 0; x < WORLD_SIZE / ratio; x++) { for (int y = 0; y < WORLD_SIZE / ratio; y++) { for (int z = 0; z < MaxZ / DZ; z++) { int index = z + x * ratio / RATIO * MaxZ + y * ratio / RATIO * WORLD_SIZE / RATIO * MaxZ; WeatherPoint reference = oldAtmosphere[index]; float evaporationRate = reference.baseEvaporationRate; float minimumHumdity = reference.baseMinimumHumditiy; float groundHeatCapacity = reference.groundHeatCapacity; float albedo = reference.albedo; float height = reference.Height; float temperature = reference.temperature; float pressure = reference.pressure; float humidity = reference.humidity; float v = reference.v; float w = reference.w; float u = reference.u; float hour = reference.hour; atmosphere[z + x * MaxZ + y * WORLD_SIZE / ratio * MaxZ] = new WeatherPoint(u, v, w, temperature, z, (y * ratio - WORLD_SIZE / 2) / ((float)WORLD_SIZE) * 180, x * ratio, y * ratio, height, albedo, evaporationRate, minimumHumdity, (x * ratio - WORLD_SIZE / 2f) / (WORLD_SIZE / 2f) * 180f, groundHeatCapacity, pressure, humidity, hour); } } } } else if (ratio > RATIO) { //The new one is less precise atmosphere = new WeatherPoint[WORLD_SIZE / ratio * WORLD_SIZE / ratio * MaxZ / DZ]; for (int x = 0; x < WORLD_SIZE / ratio; x++) { for (int y = 0; y < WORLD_SIZE / ratio; y++) { for (int z = 0; z < MaxZ / DZ; z++) { //Converts global co-ordinates to zoomed int getIndex(int _x, int _y) => _x / RATIO * MaxZ + _y / RATIO * MaxZ * WORLD_SIZE / RATIO; int sX = x * ratio; // These values are in global co-ordinates int eX = Math.Min((x + 1) * ratio, WORLD_SIZE); int sY = y * ratio; int eY = Math.Min((y + 1) * ratio, WORLD_SIZE); float evaporationRate = oldAtmosphere.GetAverage(sX, eX, sY, eY, p => p.baseEvaporationRate, getIndex); float minimumHumdity = oldAtmosphere.GetAverage(sX, eX, sY, eY, p => p.baseMinimumHumditiy, getIndex); float groundHeatCapacity = oldAtmosphere.GetAverage(sX, eX, sY, eY, p => p.groundHeatCapacity, getIndex); float albedo = oldAtmosphere.GetAverage(sX, eX, sY, eY, p => p.albedo, getIndex); float height = oldAtmosphere.GetAverage(sX, eX, sY, eY, p => p.Height, getIndex); float temperature = oldAtmosphere.GetAverage(sX, eX, sY, eY, p => p.temperature, getIndex); float pressure = oldAtmosphere.GetAverage(sX, eX, sY, eY, p => p.pressure, getIndex); float humidity = oldAtmosphere.GetAverage(sX, eX, sY, eY, p => p.humidity, getIndex); float v = oldAtmosphere.GetAverage(sX, eX, sY, eY, p => p.v, getIndex); float w = oldAtmosphere.GetAverage(sX, eX, sY, eY, p => p.w, getIndex); float u = oldAtmosphere.GetAverage(sX, eX, sY, eY, p => p.u, getIndex); float hour = oldAtmosphere.GetAverage(sX, eX, sY, eY, p => p.hour, getIndex); if (float.IsNaN(albedo)) { throw new Exception(); } if (float.IsNaN(minimumHumdity) || minimumHumdity == 0) { throw new Exception(); } int index = z + x * MaxZ + y * WORLD_SIZE / ratio * MaxZ; atmosphere[index] = new WeatherPoint(u, v, w, temperature, z, (y * ratio - WORLD_SIZE / 2) / ((float)WORLD_SIZE) * 180, x * ratio, y * ratio, height, albedo, evaporationRate, minimumHumdity, (x * ratio - WORLD_SIZE / 2f) / (WORLD_SIZE / 2f) * 180f, groundHeatCapacity, pressure, humidity, hour); } } } } else { return; } } RATIO = ratio; Trace.TraceInformation($"Generated atmosphere in {(DateTime.Now - time).TotalMilliseconds} miliseconds"); }